PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.2
WebberZone Top 10 — Popular Posts v4.5.2
4.5.2 4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 All 118 releases
top-10 / includes / frontend / class-rest-api.php

class-rest-api.php in WebberZone Top 10 — Popular Posts 4.5.2, at includes/frontend/class-rest-api.php

531 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST API class.
4 *
5 * @package WebberZone\Top_Ten\Frontend
6 */
7
8 namespace WebberZone\Top_Ten\Frontend;
9
10 use WebberZone\Top_Ten\Counter;
11
12 if ( ! defined( 'WPINC' ) ) {
13 die;
14 }
15
16 /**
17 * REST API class.
18 *
19 * @since 3.0.0
20 */
21 class REST_API extends \WP_REST_Controller {
22
23 /**
24 * Posts Route.
25 *
26 * @since 3.3.0
27 *
28 * @var string Posts Route.
29 */
30 public $posts_route;
31
32 /**
33 * Tracker Route.
34 *
35 * @since 3.3.0
36 *
37 * @var string Tracker Route.
38 */
39 public $tracker_route;
40
41 /**
42 * Counter Route.
43 *
44 * @since 4.0.0
45 *
46 * @var string Counter Route.
47 */
48 public $counter_route;
49
50 /**
51 * Main constructor.
52 *
53 * @since 3.0.0
54 */
55 public function __construct() {
56 $this->namespace = 'top-10/v1';
57 $this->posts_route = 'popular-posts';
58 $this->tracker_route = 'tracker';
59 $this->counter_route = 'counter';
60 }
61
62 /**
63 * Initialises the Top 10 REST API adding the necessary routes.
64 *
65 * @since 3.0.0
66 */
67 public function register_routes() {
68 register_rest_route(
69 $this->namespace,
70 '/' . $this->posts_route,
71 array(
72 'methods' => \WP_REST_Server::READABLE,
73 'callback' => array( $this, 'get_items' ),
74 'permission_callback' => array( $this, 'permissions_check' ),
75 'args' => $this->get_items_params(),
76 )
77 );
78 register_rest_route(
79 $this->namespace,
80 '/' . $this->posts_route . '/(?P<id>[\d]+)',
81 array(
82 'methods' => \WP_REST_Server::READABLE,
83 'callback' => array( $this, 'get_item' ),
84 'permission_callback' => array( $this, 'permissions_check' ),
85 'args' => array(
86 'id' => array(
87 'description' => __( 'Post ID.', 'top-10' ),
88 'type' => 'integer',
89 ),
90 'lang' => array(
91 'description' => __( 'TranslatePress language code to render the response in.', 'top-10' ),
92 'type' => 'string',
93 'sanitize_callback' => 'sanitize_text_field',
94 'validate_callback' => static function ( $value ) {
95 return is_string( $value ) && strlen( $value ) <= 20;
96 },
97 ),
98 ),
99 )
100 );
101 register_rest_route(
102 $this->namespace,
103 '/' . $this->tracker_route,
104 array(
105 'methods' => \WP_REST_Server::CREATABLE,
106 'callback' => array( $this, 'update_post_count' ),
107 'permission_callback' => array( $this, 'permissions_check' ),
108 'args' => $this->get_tracker_params(),
109 )
110 );
111 register_rest_route(
112 $this->namespace,
113 '/' . $this->counter_route . '/(?P<id>[\d]+)',
114 array(
115 'methods' => \WP_REST_Server::READABLE,
116 'callback' => array( $this, 'get_counter' ),
117 'permission_callback' => array( $this, 'permissions_check' ),
118 'args' => $this->get_counter_params(),
119 )
120 );
121 }
122
123 /**
124 * Check if a given request has access to get items
125 *
126 * @param \WP_REST_Request $request Full data about the request.
127 *
128 * @return \WP_Error|bool
129 */
130 public function permissions_check( \WP_REST_Request $request ) {
131 $context = $request->get_param( 'context' );
132
133 if ( 'edit' === $context && ! current_user_can( 'edit_posts' ) ) {
134 return new \WP_Error(
135 'rest_forbidden_context',
136 __( 'Sorry, you are not allowed to view this context.', 'top-10' ),
137 array( 'status' => rest_authorization_required_code() )
138 );
139 }
140
141 /**
142 * Filters the result of the permissions check for the Top 10 REST endpoints.
143 *
144 * @since 3.0.0
145 *
146 * @param bool $permission Whether the request is allowed.
147 * @param \WP_REST_Request $request The REST request object.
148 */
149 return apply_filters( 'top_ten_rest_api_permissions_check', true, $request );
150 }
151
152 /**
153 * Get popular posts.
154 *
155 * @since 3.0.0
156 *
157 * @param \WP_REST_Request $request WP Rest request.
158 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
159 */
160 public function get_items( $request ) {
161 $popular_posts = array();
162
163 $args = $request->get_params();
164
165 // Transport-only: the language must not reach the query or its cache key.
166 unset( $args['lang'] );
167
168 /**
169 * Filter the REST API arguments before they passed to get_tptn_posts().
170 *
171 * @since 3.0.0
172 *
173 * @param array $args Arguments array.
174 * @param \WP_REST_Request $request WP Rest request.
175 */
176 $args = apply_filters( 'top_ten_rest_api_get_tptn_posts_args', $args, $request );
177
178 $results = Display::get_posts( $args );
179
180 if ( ! empty( $results ) ) {
181 foreach ( $results as $popular_post ) {
182 if ( ! $this->check_read_permission( $popular_post, $request ) ) {
183 continue;
184 }
185
186 $popular_posts[] = $this->prepare_item( $popular_post, $request );
187 }
188 }
189 return rest_ensure_response( $popular_posts );
190 }
191
192 /**
193 * Get a popular post by ID. Also includes the number of views.
194 *
195 * @since 3.0.0
196 *
197 * @param \WP_REST_Request $request WP Rest request.
198 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
199 */
200 public function get_item( $request ) {
201
202 $id = $request->get_param( 'id' );
203
204 $error = new \WP_Error(
205 'rest_post_invalid_id',
206 __( 'Invalid post ID.', 'top-10' ),
207 array( 'status' => 404 )
208 );
209
210 if ( (int) $id <= 0 ) {
211 return $error;
212 }
213
214 $post = get_post( (int) $id );
215 if ( empty( $post ) || empty( $post->ID ) || ! $this->check_read_permission( $post, $request ) ) {
216 return $error;
217 }
218
219 $post = $this->prepare_item( $post, $request );
220
221 return rest_ensure_response( $post );
222 }
223
224 /**
225 * Get a popular post by ID. Also includes the number of views.
226 *
227 * @since 3.0.0
228 *
229 * @param \WP_Post $popular_post Popular Post object.
230 * @param \WP_REST_Request $request WP Rest request.
231 * @return array|mixed The formatted Popular Post object.
232 */
233 public function prepare_item( $popular_post, $request ) {
234
235 // Need to prepare items for the rest response.
236 $posts_controller = new \WP_REST_Posts_Controller( $popular_post->post_type );
237 $data = $posts_controller->prepare_item_for_response( $popular_post, $request );
238
239 // Add pageviews from popular_post object to response.
240 $visits = isset( $popular_post->visits ) ? $popular_post->visits : Counter::get_post_count_only( $popular_post->ID );
241 $data->data['visits'] = absint( $visits );
242
243 return $this->prepare_response_for_collection( $data );
244 }
245
246 /**
247 * Update post count.
248 *
249 * @since 3.0.0
250 *
251 * @param \WP_REST_Request $request WP Rest request.
252 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
253 */
254 public function update_post_count( $request ) {
255 if ( ! \WebberZone\Top_Ten\Tracker::is_tracking_request_allowed() ) {
256 $response = new \WP_REST_Response( '', 204 );
257 $response->header( 'Cache-Control', 'max-age=15, s-maxage=0' );
258 return $response;
259 }
260
261 $id = absint( $request->get_param( 'top_ten_id' ) );
262 $sitewide_context = sanitize_text_field( $request->get_param( 'top_ten_sitewide_context' ) );
263 $blog_id = absint( $request->get_param( 'top_ten_blog_id' ) );
264 $activate_counter = absint( $request->get_param( 'activate_counter' ) );
265 $top_ten_debug = absint( $request->get_param( 'top_ten_debug' ) );
266
267 $str = \WebberZone\Top_Ten\Tracker::update_count( $id, $blog_id, $activate_counter );
268 if ( '' !== $sitewide_context ) {
269 $str .= \WebberZone\Top_Ten\Tracker::update_sitewide_count( $sitewide_context, $blog_id, $activate_counter );
270 }
271
272 if ( 1 === $top_ten_debug ) {
273 return rest_ensure_response( $str );
274 } else {
275 $response = new \WP_REST_Response( '', 204 );
276 $response->header( 'Cache-Control', 'max-age=15, s-maxage=0' );
277 return $response;
278 }
279 }
280
281 /**
282 * Get the counter.
283 *
284 * @since 4.0.0
285 *
286 * @param \WP_REST_Request $request WP Rest request.
287 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
288 */
289 public function get_counter( $request ) {
290 $id = absint( $request->get_param( 'id' ) );
291
292 $error = new \WP_Error(
293 'rest_post_invalid_id',
294 __( 'Invalid post ID.', 'top-10' ),
295 array( 'status' => 404 )
296 );
297
298 if ( (int) $id <= 0 ) {
299 return $error;
300 }
301
302 $args = array();
303
304 $counter = sanitize_text_field( $request->get_param( 'counter' ) ) ? sanitize_text_field( $request->get_param( 'counter' ) ) : 'total';
305 $blog_id = absint( $request->get_param( 'blog_id' ) ) ? absint( $request->get_param( 'blog_id' ) ) : get_current_blog_id();
306
307 $from_date = sanitize_text_field( $request->get_param( 'from_date' ) );
308 $to_date = sanitize_text_field( $request->get_param( 'to_date' ) );
309
310 if ( ! empty( $from_date ) ) {
311 $args['from_date'] = $from_date;
312 }
313 if ( ! empty( $to_date ) ) {
314 $args['to_date'] = $to_date;
315 }
316
317 $counter = absint( Counter::get_post_count_only( (int) $id, $counter, $blog_id, $args ) );
318
319 return rest_ensure_response( $counter );
320 }
321
322 /**
323 * Get the arguments for fetching the popular posts.
324 *
325 * @since 3.0.0
326 *
327 * @return array Top 10 REST API popular posts arguments.
328 */
329 public function get_items_params() {
330 $args = array(
331 'limit' => array(
332 'description' => esc_html__( 'Number of posts', 'top-10' ),
333 'type' => 'integer',
334 'sanitize_callback' => 'absint',
335 ),
336 'post_types' => array(
337 'description' => esc_html__( 'Post types', 'top-10' ),
338 'type' => 'string',
339 ),
340 'context' => array(
341 'description' => esc_html__( 'Scope under which the request is made; determines fields present in response.', 'top-10' ),
342 'type' => 'string',
343 'enum' => array( 'view', 'embed', 'edit' ),
344 'default' => 'view',
345 ),
346 'lang' => array(
347 'description' => esc_html__( 'TranslatePress language code to render the response in.', 'top-10' ),
348 'type' => 'string',
349 'sanitize_callback' => 'sanitize_text_field',
350 'validate_callback' => static function ( $value ) {
351 return is_string( $value ) && strlen( $value ) <= 20;
352 },
353 ),
354 );
355
356 /**
357 * Filters the query parameters accepted by the popular posts REST endpoint.
358 *
359 * @since 3.0.0
360 *
361 * @param array $args Collection parameters, keyed by parameter name.
362 */
363 return apply_filters( 'top_ten_rest_api_get_items_params', $args );
364 }
365
366 /**
367 * Get the arguments for tracking posts.
368 *
369 * @since 3.0.0
370 *
371 * @return array Top 10 REST API popular posts arguments.
372 */
373 public function get_tracker_params() {
374 $args = array(
375 'top_ten_id' => array(
376 'description' => esc_html__( 'ID of the post.', 'top-10' ),
377 'type' => 'integer',
378 'sanitize_callback' => 'absint',
379 ),
380 'top_ten_sitewide_context' => array(
381 'description' => esc_html__( 'Site-wide tracking context: front_page, home_page, search, category, tag, taxonomy, author, post_type_archive, date_archive, archive or other. Each key maps to a fixed reserved ID in the shared count tables.', 'top-10' ),
382 'type' => 'string',
383 'sanitize_callback' => 'sanitize_text_field',
384 ),
385 'top_ten_blog_id' => array(
386 'description' => esc_html__( 'Blog ID of the post.', 'top-10' ),
387 'type' => 'integer',
388 'sanitize_callback' => 'absint',
389 ),
390 'activate_counter' => array(
391 'description' => esc_html__( 'Activate counter flag.', 'top-10' ),
392 'type' => 'integer',
393 'sanitize_callback' => 'absint',
394 ),
395 'top_ten_debug' => array(
396 'description' => esc_html__( 'Debug flag.', 'top-10' ),
397 'type' => 'integer',
398 'sanitize_callback' => 'absint',
399 ),
400 );
401
402 /**
403 * Filters the query parameters accepted by the tracker REST endpoint.
404 *
405 * @since 3.0.0
406 *
407 * @param array $args Collection parameters, keyed by parameter name.
408 */
409 return apply_filters( 'top_ten_rest_api_get_tracker_params', $args );
410 }
411
412 /**
413 * Get counter params.
414 *
415 * @since 4.0.0
416 *
417 * @return array Top 10 REST API counter arguments.
418 */
419 public function get_counter_params() {
420 $args = array(
421 'id' => array(
422 'description' => esc_html__( 'ID of the post.', 'top-10' ),
423 'type' => 'integer',
424 'sanitize_callback' => 'absint',
425 ),
426 'counter' => array(
427 'description' => esc_html__( 'Counter type.', 'top-10' ),
428 'type' => 'string',
429 'sanitize_callback' => 'sanitize_text_field',
430 ),
431 'blog_id' => array(
432 'description' => esc_html__( 'Blog ID.', 'top-10' ),
433 'type' => 'integer',
434 'sanitize_callback' => 'absint',
435 ),
436 'from_date' => array(
437 'description' => esc_html__( 'From date.', 'top-10' ),
438 'type' => 'string',
439 'sanitize_callback' => 'sanitize_text_field',
440 ),
441 'to_date' => array(
442 'description' => esc_html__( 'To date.', 'top-10' ),
443 'type' => 'string',
444 'sanitize_callback' => 'sanitize_text_field',
445 ),
446 );
447
448 /**
449 * Filters the query parameters accepted by the counter REST endpoint.
450 *
451 * @since 3.4.0-RC1
452 *
453 * @param array $args Collection parameters, keyed by parameter name.
454 */
455 return apply_filters( 'top_ten_rest_api_get_counter_params', $args );
456 }
457
458 /**
459 * Checks if a given post type can be viewed or managed.
460 *
461 * @since 3.0.0
462 *
463 * @param \WP_Post_Type|string $post_type Post type name or object.
464 * @return bool Whether the post type is allowed in REST.
465 */
466 protected function check_is_post_type_allowed( $post_type ) {
467 if ( ! is_object( $post_type ) ) {
468 $post_type = get_post_type_object( $post_type );
469 }
470
471 if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) {
472 return true;
473 }
474
475 return false;
476 }
477
478 /**
479 * Checks if a post can be read.
480 *
481 * Correctly handles posts with the inherit status.
482 *
483 * @since 3.0.0
484 *
485 * @param \WP_Post $post Post object.
486 * @param \WP_REST_Request $request WP Rest request.
487 * @return bool Whether the post can be read.
488 */
489 public function check_read_permission( $post, $request = null ) {
490 $post_type = get_post_type_object( $post->post_type );
491 if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
492 return false;
493 }
494
495 // If context is 'edit', require edit permissions to prevent exposing sensitive data like passwords.
496 if ( $request && 'edit' === $request->get_param( 'context' ) ) {
497 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
498 return false;
499 }
500 }
501
502 // Is the post readable?
503 if ( 'publish' === $post->post_status || current_user_can( 'read_post', $post->ID ) ) {
504 return true;
505 }
506
507 $post_status_obj = get_post_status_object( $post->post_status );
508 if ( $post_status_obj && $post_status_obj->public ) {
509 return true;
510 }
511
512 // Can we read the parent if we're inheriting?
513 if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
514 $parent = get_post( $post->post_parent );
515 if ( $parent ) {
516 return $this->check_read_permission( $parent, $request );
517 }
518 }
519
520 /*
521 * If there isn't a parent, but the status is set to inherit, assume
522 * it's published (as per get_post_status()).
523 */
524 if ( 'inherit' === $post->post_status ) {
525 return true;
526 }
527
528 return false;
529 }
530 }
531