PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.1
WebberZone Top 10 — Popular Posts v4.5.1
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 1.6.1 All 117 releases
top-10 / includes / frontend / class-rest-api.php

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

512 lines 14.0 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 ),
91 )
92 );
93 register_rest_route(
94 $this->namespace,
95 '/' . $this->tracker_route,
96 array(
97 'methods' => \WP_REST_Server::CREATABLE,
98 'callback' => array( $this, 'update_post_count' ),
99 'permission_callback' => array( $this, 'permissions_check' ),
100 'args' => $this->get_tracker_params(),
101 )
102 );
103 register_rest_route(
104 $this->namespace,
105 '/' . $this->counter_route . '/(?P<id>[\d]+)',
106 array(
107 'methods' => \WP_REST_Server::READABLE,
108 'callback' => array( $this, 'get_counter' ),
109 'permission_callback' => array( $this, 'permissions_check' ),
110 'args' => $this->get_counter_params(),
111 )
112 );
113 }
114
115 /**
116 * Check if a given request has access to get items
117 *
118 * @param \WP_REST_Request $request Full data about the request.
119 *
120 * @return \WP_Error|bool
121 */
122 public function permissions_check( \WP_REST_Request $request ) {
123 $context = $request->get_param( 'context' );
124
125 if ( 'edit' === $context && ! current_user_can( 'edit_posts' ) ) {
126 return new \WP_Error(
127 'rest_forbidden_context',
128 __( 'Sorry, you are not allowed to view this context.', 'top-10' ),
129 array( 'status' => rest_authorization_required_code() )
130 );
131 }
132
133 /**
134 * Filters the result of the permissions check for the Top 10 REST endpoints.
135 *
136 * @since 3.0.0
137 *
138 * @param bool $permission Whether the request is allowed.
139 * @param \WP_REST_Request $request The REST request object.
140 */
141 return apply_filters( 'top_ten_rest_api_permissions_check', true, $request );
142 }
143
144 /**
145 * Get popular posts.
146 *
147 * @since 3.0.0
148 *
149 * @param \WP_REST_Request $request WP Rest request.
150 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
151 */
152 public function get_items( $request ) {
153 $popular_posts = array();
154
155 $args = $request->get_params();
156
157 /**
158 * Filter the REST API arguments before they passed to get_tptn_posts().
159 *
160 * @since 3.0.0
161 *
162 * @param array $args Arguments array.
163 * @param \WP_REST_Request $request WP Rest request.
164 */
165 $args = apply_filters( 'top_ten_rest_api_get_tptn_posts_args', $args, $request );
166
167 $results = Display::get_posts( $args );
168
169 if ( ! empty( $results ) ) {
170 foreach ( $results as $popular_post ) {
171 if ( ! $this->check_read_permission( $popular_post, $request ) ) {
172 continue;
173 }
174
175 $popular_posts[] = $this->prepare_item( $popular_post, $request );
176 }
177 }
178 return rest_ensure_response( $popular_posts );
179 }
180
181 /**
182 * Get a popular post by ID. Also includes the number of views.
183 *
184 * @since 3.0.0
185 *
186 * @param \WP_REST_Request $request WP Rest request.
187 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
188 */
189 public function get_item( $request ) {
190
191 $id = $request->get_param( 'id' );
192
193 $error = new \WP_Error(
194 'rest_post_invalid_id',
195 __( 'Invalid post ID.', 'top-10' ),
196 array( 'status' => 404 )
197 );
198
199 if ( (int) $id <= 0 ) {
200 return $error;
201 }
202
203 $post = get_post( (int) $id );
204 if ( empty( $post ) || empty( $post->ID ) || ! $this->check_read_permission( $post, $request ) ) {
205 return $error;
206 }
207
208 $post = $this->prepare_item( $post, $request );
209
210 return rest_ensure_response( $post );
211 }
212
213 /**
214 * Get a popular post by ID. Also includes the number of views.
215 *
216 * @since 3.0.0
217 *
218 * @param \WP_Post $popular_post Popular Post object.
219 * @param \WP_REST_Request $request WP Rest request.
220 * @return array|mixed The formatted Popular Post object.
221 */
222 public function prepare_item( $popular_post, $request ) {
223
224 // Need to prepare items for the rest response.
225 $posts_controller = new \WP_REST_Posts_Controller( $popular_post->post_type );
226 $data = $posts_controller->prepare_item_for_response( $popular_post, $request );
227
228 // Add pageviews from popular_post object to response.
229 $visits = isset( $popular_post->visits ) ? $popular_post->visits : Counter::get_post_count_only( $popular_post->ID );
230 $data->data['visits'] = absint( $visits );
231
232 return $this->prepare_response_for_collection( $data );
233 }
234
235 /**
236 * Update post count.
237 *
238 * @since 3.0.0
239 *
240 * @param \WP_REST_Request $request WP Rest request.
241 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
242 */
243 public function update_post_count( $request ) {
244 if ( ! \WebberZone\Top_Ten\Tracker::is_tracking_request_allowed() ) {
245 $response = new \WP_REST_Response( '', 204 );
246 $response->header( 'Cache-Control', 'max-age=15, s-maxage=0' );
247 return $response;
248 }
249
250 $id = absint( $request->get_param( 'top_ten_id' ) );
251 $sitewide_context = sanitize_text_field( $request->get_param( 'top_ten_sitewide_context' ) );
252 $blog_id = absint( $request->get_param( 'top_ten_blog_id' ) );
253 $activate_counter = absint( $request->get_param( 'activate_counter' ) );
254 $top_ten_debug = absint( $request->get_param( 'top_ten_debug' ) );
255
256 $str = \WebberZone\Top_Ten\Tracker::update_count( $id, $blog_id, $activate_counter );
257 if ( '' !== $sitewide_context ) {
258 $str .= \WebberZone\Top_Ten\Tracker::update_sitewide_count( $sitewide_context, $blog_id, $activate_counter );
259 }
260
261 if ( 1 === $top_ten_debug ) {
262 return rest_ensure_response( $str );
263 } else {
264 $response = new \WP_REST_Response( '', 204 );
265 $response->header( 'Cache-Control', 'max-age=15, s-maxage=0' );
266 return $response;
267 }
268 }
269
270 /**
271 * Get the counter.
272 *
273 * @since 4.0.0
274 *
275 * @param \WP_REST_Request $request WP Rest request.
276 * @return mixed|\WP_REST_Response Array of post objects or post IDs.
277 */
278 public function get_counter( $request ) {
279 $id = absint( $request->get_param( 'id' ) );
280
281 $error = new \WP_Error(
282 'rest_post_invalid_id',
283 __( 'Invalid post ID.', 'top-10' ),
284 array( 'status' => 404 )
285 );
286
287 if ( (int) $id <= 0 ) {
288 return $error;
289 }
290
291 $args = array();
292
293 $counter = sanitize_text_field( $request->get_param( 'counter' ) ) ? sanitize_text_field( $request->get_param( 'counter' ) ) : 'total';
294 $blog_id = absint( $request->get_param( 'blog_id' ) ) ? absint( $request->get_param( 'blog_id' ) ) : get_current_blog_id();
295
296 $from_date = sanitize_text_field( $request->get_param( 'from_date' ) );
297 $to_date = sanitize_text_field( $request->get_param( 'to_date' ) );
298
299 if ( ! empty( $from_date ) ) {
300 $args['from_date'] = $from_date;
301 }
302 if ( ! empty( $to_date ) ) {
303 $args['to_date'] = $to_date;
304 }
305
306 $counter = absint( Counter::get_post_count_only( (int) $id, $counter, $blog_id, $args ) );
307
308 return rest_ensure_response( $counter );
309 }
310
311 /**
312 * Get the arguments for fetching the popular posts.
313 *
314 * @since 3.0.0
315 *
316 * @return array Top 10 REST API popular posts arguments.
317 */
318 public function get_items_params() {
319 $args = array(
320 'limit' => array(
321 'description' => esc_html__( 'Number of posts', 'top-10' ),
322 'type' => 'integer',
323 'sanitize_callback' => 'absint',
324 ),
325 'post_types' => array(
326 'description' => esc_html__( 'Post types', 'top-10' ),
327 'type' => 'string',
328 ),
329 'context' => array(
330 'description' => esc_html__( 'Scope under which the request is made; determines fields present in response.', 'top-10' ),
331 'type' => 'string',
332 'enum' => array( 'view', 'embed', 'edit' ),
333 'default' => 'view',
334 ),
335 );
336
337 /**
338 * Filters the query parameters accepted by the popular posts REST endpoint.
339 *
340 * @since 3.0.0
341 *
342 * @param array $args Collection parameters, keyed by parameter name.
343 */
344 return apply_filters( 'top_ten_rest_api_get_items_params', $args );
345 }
346
347 /**
348 * Get the arguments for tracking posts.
349 *
350 * @since 3.0.0
351 *
352 * @return array Top 10 REST API popular posts arguments.
353 */
354 public function get_tracker_params() {
355 $args = array(
356 'top_ten_id' => array(
357 'description' => esc_html__( 'ID of the post.', 'top-10' ),
358 'type' => 'integer',
359 'sanitize_callback' => 'absint',
360 ),
361 'top_ten_sitewide_context' => array(
362 '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' ),
363 'type' => 'string',
364 'sanitize_callback' => 'sanitize_text_field',
365 ),
366 'top_ten_blog_id' => array(
367 'description' => esc_html__( 'Blog ID of the post.', 'top-10' ),
368 'type' => 'integer',
369 'sanitize_callback' => 'absint',
370 ),
371 'activate_counter' => array(
372 'description' => esc_html__( 'Activate counter flag.', 'top-10' ),
373 'type' => 'integer',
374 'sanitize_callback' => 'absint',
375 ),
376 'top_ten_debug' => array(
377 'description' => esc_html__( 'Debug flag.', 'top-10' ),
378 'type' => 'integer',
379 'sanitize_callback' => 'absint',
380 ),
381 );
382
383 /**
384 * Filters the query parameters accepted by the tracker REST endpoint.
385 *
386 * @since 3.0.0
387 *
388 * @param array $args Collection parameters, keyed by parameter name.
389 */
390 return apply_filters( 'top_ten_rest_api_get_tracker_params', $args );
391 }
392
393 /**
394 * Get counter params.
395 *
396 * @since 4.0.0
397 *
398 * @return array Top 10 REST API counter arguments.
399 */
400 public function get_counter_params() {
401 $args = array(
402 'id' => array(
403 'description' => esc_html__( 'ID of the post.', 'top-10' ),
404 'type' => 'integer',
405 'sanitize_callback' => 'absint',
406 ),
407 'counter' => array(
408 'description' => esc_html__( 'Counter type.', 'top-10' ),
409 'type' => 'string',
410 'sanitize_callback' => 'sanitize_text_field',
411 ),
412 'blog_id' => array(
413 'description' => esc_html__( 'Blog ID.', 'top-10' ),
414 'type' => 'integer',
415 'sanitize_callback' => 'absint',
416 ),
417 'from_date' => array(
418 'description' => esc_html__( 'From date.', 'top-10' ),
419 'type' => 'string',
420 'sanitize_callback' => 'sanitize_text_field',
421 ),
422 'to_date' => array(
423 'description' => esc_html__( 'To date.', 'top-10' ),
424 'type' => 'string',
425 'sanitize_callback' => 'sanitize_text_field',
426 ),
427 );
428
429 /**
430 * Filters the query parameters accepted by the counter REST endpoint.
431 *
432 * @since 3.4.0-RC1
433 *
434 * @param array $args Collection parameters, keyed by parameter name.
435 */
436 return apply_filters( 'top_ten_rest_api_get_counter_params', $args );
437 }
438
439 /**
440 * Checks if a given post type can be viewed or managed.
441 *
442 * @since 3.0.0
443 *
444 * @param \WP_Post_Type|string $post_type Post type name or object.
445 * @return bool Whether the post type is allowed in REST.
446 */
447 protected function check_is_post_type_allowed( $post_type ) {
448 if ( ! is_object( $post_type ) ) {
449 $post_type = get_post_type_object( $post_type );
450 }
451
452 if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) {
453 return true;
454 }
455
456 return false;
457 }
458
459 /**
460 * Checks if a post can be read.
461 *
462 * Correctly handles posts with the inherit status.
463 *
464 * @since 3.0.0
465 *
466 * @param \WP_Post $post Post object.
467 * @param \WP_REST_Request $request WP Rest request.
468 * @return bool Whether the post can be read.
469 */
470 public function check_read_permission( $post, $request = null ) {
471 $post_type = get_post_type_object( $post->post_type );
472 if ( ! $this->check_is_post_type_allowed( $post_type ) ) {
473 return false;
474 }
475
476 // If context is 'edit', require edit permissions to prevent exposing sensitive data like passwords.
477 if ( $request && 'edit' === $request->get_param( 'context' ) ) {
478 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
479 return false;
480 }
481 }
482
483 // Is the post readable?
484 if ( 'publish' === $post->post_status || current_user_can( 'read_post', $post->ID ) ) {
485 return true;
486 }
487
488 $post_status_obj = get_post_status_object( $post->post_status );
489 if ( $post_status_obj && $post_status_obj->public ) {
490 return true;
491 }
492
493 // Can we read the parent if we're inheriting?
494 if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
495 $parent = get_post( $post->post_parent );
496 if ( $parent ) {
497 return $this->check_read_permission( $parent, $request );
498 }
499 }
500
501 /*
502 * If there isn't a parent, but the status is set to inherit, assume
503 * it's published (as per get_post_status()).
504 */
505 if ( 'inherit' === $post->post_status ) {
506 return true;
507 }
508
509 return false;
510 }
511 }
512