PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-stats-admin/src/class-rest-controller.php +1459 -0 16.2-beta → 16.3-beta View file →
@@ -1,0 +1,1459 @@
1 +<?php
2 +/**
3 + * The Stats Rest Controller class.
4 + * Registers the REST routes for Odyssey Stats.
5 + *
6 + * @package automattic/jetpack-stats-admin
7 + */
8 +
9 +namespace Automattic\Jetpack\Stats_Admin;
10 +
11 +use Automattic\Jetpack\Constants;
12 +use Automattic\Jetpack\Stats\Settings as Stats_Settings;
13 +use Automattic\Jetpack\Stats\WPCOM_Stats;
14 +use Jetpack_Options;
15 +use WP_Error;
16 +use WP_REST_Request;
17 +use WP_REST_Server;
18 +
19 +/**
20 + * Registers the REST routes for Stats.
21 + * It bascially forwards the requests to the WordPress.com REST API.
22 + */
23 +class REST_Controller {
24 + const JETPACK_STATS_DASHBOARD_MODULES_CACHE_KEY = 'jetpack_stats_dashboard_modules_cache_key';
25 + const JETPACK_STATS_DASHBOARD_MODULE_SETTINGS_CACHE_KEY = 'jetpack_stats_dashboard_module_settings_cache_key';
26 +
27 + /**
28 + * Namespace for the REST API.
29 + *
30 + * @var string
31 + */
32 + public static $namespace = 'jetpack/v4/stats-app';
33 +
34 + /**
35 + * Hold an instance of WPCOM_Stats.
36 + *
37 + * @var WPCOM_Stats
38 + */
39 + protected $wpcom_stats;
40 +
41 + /**
42 + * Constructor
43 + */
44 + public function __construct() {
45 + $this->wpcom_stats = new WPCOM_Stats();
46 + }
47 +
48 + /**
49 + * Registers the REST routes on the `rest_api_init` hook.
50 + *
51 + * Instantiated here, rather than eagerly, so the controller class only loads
52 + * on requests that reach `rest_api_init`. Static so the callback can be
53 + * unregistered.
54 + *
55 + * @access public
56 + */
57 + public static function register() {
58 + ( new self() )->register_rest_routes();
59 + }
60 +
61 + /**
62 + * Registers the REST routes for Odyssey Stats.
63 + *
64 + * Odyssey Stats is built from `wp-calypso`, which leverages the `public-api.wordpress.com` API.
65 + * The current Site ID is added as part of the route, so that the front end doesn't have to handle the differences.
66 + *
67 + * @access public
68 + * @static
69 + */
70 + public function register_rest_routes() {
71 + // Stats for single resource type.
72 + register_rest_route(
73 + static::$namespace,
74 + sprintf( '/sites/%d/stats/(?P<resource>[\-\w]+)/(?P<resource_id>[\d]+)', Jetpack_Options::get_option( 'id' ) ),
75 + array(
76 + 'methods' => WP_REST_Server::READABLE,
77 + 'callback' => array( $this, 'get_single_resource_stats' ),
78 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
79 + )
80 + );
81 +
82 + // Stats for a resource type.
83 + register_rest_route(
84 + static::$namespace,
85 + sprintf( '/sites/%d/stats/(?P<resource>[\-\w]+)', Jetpack_Options::get_option( 'id' ) ),
86 + array(
87 + 'methods' => WP_REST_Server::READABLE,
88 + 'callback' => array( $this, 'get_stats_resource' ),
89 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
90 + )
91 + );
92 +
93 + // Single post info.
94 + register_rest_route(
95 + static::$namespace,
96 + sprintf( '/sites/%d/posts/(?P<resource_id>[\d]+)', Jetpack_Options::get_option( 'id' ) ),
97 + array(
98 + 'methods' => WP_REST_Server::READABLE,
99 + 'callback' => array( $this, 'get_single_post' ),
100 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
101 + )
102 + );
103 +
104 + // Single post likes.
105 + register_rest_route(
106 + static::$namespace,
107 + sprintf( '/sites/%d/posts/(?P<resource_id>[\d]+)/likes', Jetpack_Options::get_option( 'id' ) ),
108 + array(
109 + 'methods' => WP_REST_Server::READABLE,
110 + 'callback' => array( $this, 'get_single_post_likes' ),
111 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
112 + )
113 + );
114 +
115 + // General stats for the site.
116 + register_rest_route(
117 + static::$namespace,
118 + sprintf( '/sites/%d/stats', Jetpack_Options::get_option( 'id' ) ),
119 + array(
120 + 'methods' => WP_REST_Server::READABLE,
121 + 'callback' => array( $this, 'get_site_stats' ),
122 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
123 + )
124 + );
125 +
126 + // Whether site has never published post / page.
127 + register_rest_route(
128 + static::$namespace,
129 + sprintf( '/sites/%d/site-has-never-published-post', Jetpack_Options::get_option( 'id' ) ),
130 + array(
131 + 'methods' => WP_REST_Server::READABLE,
132 + 'callback' => array( $this, 'site_has_never_published_post' ),
133 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
134 + )
135 + );
136 +
137 + // List posts.
138 + register_rest_route(
139 + static::$namespace,
140 + sprintf( '/sites/%d/posts', Jetpack_Options::get_option( 'id' ) ),
141 + array(
142 + 'methods' => WP_REST_Server::READABLE,
143 + 'callback' => array( $this, 'get_site_posts' ),
144 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
145 + )
146 + );
147 +
148 + // Subscribers counts.
149 + register_rest_route(
150 + static::$namespace,
151 + sprintf( '/sites/%d/subscribers/counts', Jetpack_Options::get_option( 'id' ) ),
152 + array(
153 + 'methods' => WP_REST_Server::READABLE,
154 + 'callback' => array( $this, 'get_site_subscribers_counts' ),
155 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
156 + )
157 + );
158 +
159 + // Stats Plan Usage.
160 + register_rest_route(
161 + static::$namespace,
162 + sprintf( '/sites/%d/jetpack-stats/usage', Jetpack_Options::get_option( 'id' ) ),
163 + array(
164 + 'methods' => WP_REST_Server::READABLE,
165 + 'callback' => array( $this, 'get_site_plan_usage' ),
166 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
167 + )
168 + );
169 +
170 + // Stats settings.
171 + register_rest_route(
172 + static::$namespace,
173 + sprintf( '/sites/%d/jetpack-stats/settings', Jetpack_Options::get_option( 'id' ) ),
174 + array(
175 + array(
176 + 'methods' => WP_REST_Server::READABLE,
177 + 'callback' => array( $this, 'get_stats_settings' ),
178 + 'permission_callback' => array( $this, 'can_user_manage_stats_settings_callback' ),
179 + ),
180 + array(
181 + 'methods' => WP_REST_Server::EDITABLE,
182 + 'callback' => array( $this, 'update_stats_settings' ),
183 + 'permission_callback' => array( $this, 'can_user_manage_stats_settings_callback' ),
184 + 'args' => array(
185 + 'admin_bar' => array(
186 + 'description' => 'Show a chart of the last 48 hours of views in the admin bar',
187 + 'type' => 'boolean',
188 + ),
189 + 'roles' => array(
190 + 'description' => 'Roles that can view Stats. `administrator` is always kept.',
191 + 'type' => 'array',
192 + 'items' => array( 'type' => 'string' ),
193 + 'minItems' => 1,
194 + ),
195 + 'count_roles' => array(
196 + 'description' => 'Roles whose logged-in page views are counted',
197 + 'type' => 'array',
198 + 'items' => array( 'type' => 'string' ),
199 + ),
200 + 'wpcom_reader_views_enabled' => array(
201 + 'description' => 'Show post views in the WordPress.com Reader',
202 + 'type' => 'boolean',
203 + ),
204 + ),
205 + ),
206 + )
207 + );
208 +
209 + // User feedback endpoint.
210 + register_rest_route(
211 + static::$namespace,
212 + sprintf( '/sites/%d/jetpack-stats/user-feedback', Jetpack_Options::get_option( 'id' ) ),
213 + array(
214 + 'methods' => WP_REST_Server::CREATABLE,
215 + 'callback' => array( $this, 'post_user_feedback' ),
216 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
217 + )
218 + );
219 +
220 + // WordAds Earnings.
221 + register_rest_route(
222 + static::$namespace,
223 + sprintf( '/sites/%d/wordads/earnings', Jetpack_Options::get_option( 'id' ) ),
224 + array(
225 + 'methods' => WP_REST_Server::READABLE,
226 + 'callback' => array( $this, 'get_wordads_earnings' ),
227 + 'permission_callback' => array( $this, 'can_user_view_wordads_stats_callback' ),
228 + )
229 + );
230 +
231 + // WordAds Stats.
232 + register_rest_route(
233 + static::$namespace,
234 + sprintf( '/sites/%d/wordads/stats', Jetpack_Options::get_option( 'id' ) ),
235 + array(
236 + 'methods' => WP_REST_Server::READABLE,
237 + 'callback' => array( $this, 'get_wordads_stats' ),
238 + 'permission_callback' => array( $this, 'can_user_view_wordads_stats_callback' ),
239 + )
240 + );
241 +
242 + // Legacy: Update Stats notices.
243 + // TODO: remove this in the next release.
244 + register_rest_route(
245 + static::$namespace,
246 + '/stats/notices',
247 + array(
248 + 'methods' => WP_REST_Server::EDITABLE,
249 + 'callback' => array( $this, 'update_notice_status' ),
250 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
251 + 'args' => array(
252 + 'id' => array(
253 + 'required' => true,
254 + 'type' => 'string',
255 + 'description' => 'ID of the notice',
256 + ),
257 + 'status' => array(
258 + 'required' => true,
259 + 'type' => 'string',
260 + 'description' => 'Status of the notice',
261 + ),
262 + 'postponed_for' => array(
263 + 'type' => 'number',
264 + 'default' => 0,
265 + 'description' => 'Postponed for (in seconds)',
266 + 'minimum' => 0,
267 + ),
268 + ),
269 + )
270 + );
271 +
272 + // Update Stats notices.
273 + register_rest_route(
274 + static::$namespace,
275 + sprintf( '/sites/%d/jetpack-stats-dashboard/notices', Jetpack_Options::get_option( 'id' ) ),
276 + array(
277 + 'methods' => WP_REST_Server::EDITABLE,
278 + 'callback' => array( $this, 'update_notice_status' ),
279 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
280 + 'args' => array(
281 + 'id' => array(
282 + 'required' => true,
283 + 'type' => 'string',
284 + 'description' => 'ID of the notice',
285 + ),
286 + 'status' => array(
287 + 'required' => true,
288 + 'type' => 'string',
289 + 'description' => 'Status of the notice',
290 + ),
291 + 'postponed_for' => array(
292 + 'type' => 'number',
293 + // Forwarded to WPCOM as-is, whose schema rejects the null an omitted param would carry.
294 + 'default' => 0,
295 + 'description' => 'Postponed for (in seconds)',
296 + 'minimum' => 0,
297 + ),
298 + ),
299 + )
300 + );
301 +
302 + // Get Stats notices.
303 + register_rest_route(
304 + static::$namespace,
305 + sprintf( '/sites/%d/jetpack-stats-dashboard/notices', Jetpack_Options::get_option( 'id' ) ),
306 + array(
307 + 'methods' => WP_REST_Server::READABLE,
308 + 'callback' => array( $this, 'get_notice_status' ),
309 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
310 + 'args' => array(
311 + 'include_details' => array(
312 + 'type' => 'boolean',
313 + 'default' => false,
314 + 'description' => 'Return a detail record per notice instead of a flat boolean map',
315 + ),
316 + ),
317 + )
318 + );
319 +
320 + // Get referrer spam list.
321 + register_rest_route(
322 + static::$namespace,
323 + sprintf( '/sites/%d/stats/referrers/spam', Jetpack_Options::get_option( 'id' ) ),
324 + array(
325 + 'methods' => WP_REST_Server::READABLE,
326 + 'callback' => array( $this, 'get_referrer_spam_list' ),
327 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
328 + )
329 + );
330 +
331 + // Mark referrer spam.
332 + register_rest_route(
333 + static::$namespace,
334 + sprintf( '/sites/%d/stats/referrers/spam/new', Jetpack_Options::get_option( 'id' ) ),
335 + array(
336 + 'methods' => WP_REST_Server::EDITABLE,
337 + 'callback' => array( $this, 'mark_referrer_spam' ),
338 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
339 + 'args' => array(
340 + 'domain' => array(
341 + 'required' => true,
342 + 'type' => 'string',
343 + 'description' => 'Domain of the referrer',
344 + ),
345 + ),
346 + )
347 + );
348 +
349 + // Unmark referrer spam.
350 + register_rest_route(
351 + static::$namespace,
352 + sprintf( '/sites/%d/stats/referrers/spam/delete', Jetpack_Options::get_option( 'id' ) ),
353 + array(
354 + 'methods' => WP_REST_Server::EDITABLE,
355 + 'callback' => array( $this, 'unmark_referrer_spam' ),
356 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
357 + 'args' => array(
358 + 'domain' => array(
359 + 'required' => true,
360 + 'type' => 'string',
361 + 'description' => 'Domain of the referrer',
362 + ),
363 + ),
364 + )
365 + );
366 +
367 + // Update dashboard modules.
368 + register_rest_route(
369 + static::$namespace,
370 + sprintf( '/sites/%d/jetpack-stats-dashboard/modules', Jetpack_Options::get_option( 'id' ) ),
371 + array(
372 + 'methods' => WP_REST_Server::EDITABLE,
373 + 'callback' => array( $this, 'update_dashboard_modules' ),
374 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
375 + )
376 + );
377 +
378 + // Get dashboard modules.
379 + register_rest_route(
380 + static::$namespace,
381 + sprintf( '/sites/%d/jetpack-stats-dashboard/modules', Jetpack_Options::get_option( 'id' ) ),
382 + array(
383 + 'methods' => WP_REST_Server::READABLE,
384 + 'callback' => array( $this, 'get_dashboard_modules' ),
385 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
386 + )
387 + );
388 +
389 + // Update dashboard module settings.
390 + register_rest_route(
391 + static::$namespace,
392 + sprintf( '/sites/%d/jetpack-stats-dashboard/module-settings', Jetpack_Options::get_option( 'id' ) ),
393 + array(
394 + 'methods' => WP_REST_Server::EDITABLE,
395 + 'callback' => array( $this, 'update_dashboard_module_settings' ),
396 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
397 + )
398 + );
399 +
400 + // Get dashboard module settings.
401 + register_rest_route(
402 + static::$namespace,
403 + sprintf( '/sites/%d/jetpack-stats-dashboard/module-settings', Jetpack_Options::get_option( 'id' ) ),
404 + array(
405 + 'methods' => WP_REST_Server::READABLE,
406 + 'callback' => array( $this, 'get_dashboard_module_settings' ),
407 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
408 + )
409 + );
410 +
411 + // Get email stats as a list.
412 + register_rest_route(
413 + static::$namespace,
414 + sprintf( '/sites/%d/stats/emails/(?P<resource>[\-\w\d]+)', Jetpack_Options::get_option( 'id' ) ),
415 + array(
416 + 'methods' => WP_REST_Server::READABLE,
417 + 'callback' => array( $this, 'get_email_stats_list' ),
418 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
419 + )
420 + );
421 +
422 + // Get Email opens stats for a single post.
423 + register_rest_route(
424 + static::$namespace,
425 + sprintf( '/sites/%d/stats/opens/emails/(?P<post_id>[\d]+)/(?P<resource>[\-\w]+)', Jetpack_Options::get_option( 'id' ) ),
426 + array(
427 + 'methods' => WP_REST_Server::READABLE,
428 + 'callback' => array( $this, 'get_email_opens_stats_single' ),
429 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
430 + )
431 + );
432 +
433 + // Get Email clicks stats for a single post.
434 + register_rest_route(
435 + static::$namespace,
436 + sprintf( '/sites/%d/stats/clicks/emails/(?P<post_id>[\d]+)/(?P<resource>[\-\w]+)', Jetpack_Options::get_option( 'id' ) ),
437 + array(
438 + 'methods' => WP_REST_Server::READABLE,
439 + 'callback' => array( $this, 'get_email_clicks_stats_single' ),
440 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
441 + )
442 + );
443 +
444 + // Get Email stats time series.
445 + register_rest_route(
446 + static::$namespace,
447 + sprintf( '/sites/%d/stats/(?P<resource>[\-\w]+)/emails/(?P<post_id>[\d]+)', Jetpack_Options::get_option( 'id' ) ),
448 + array(
449 + 'methods' => WP_REST_Server::READABLE,
450 + 'callback' => array( $this, 'get_email_stats_time_series' ),
451 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
452 + )
453 + );
454 +
455 + // Get UTM stats time series.
456 + register_rest_route(
457 + static::$namespace,
458 + // /stats/utm/utm_campaign,utm_source,utm_medium
459 + sprintf( '/sites/%d/stats/utm/(?P<utm_params>[_,\-\w]+)', Jetpack_Options::get_option( 'id' ) ),
460 + array(
461 + 'methods' => WP_REST_Server::READABLE,
462 + 'callback' => array( $this, 'get_utm_stats_time_series' ),
463 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
464 + )
465 + );
466 +
467 + // Get Devices stats time series.
468 + register_rest_route(
469 + static::$namespace,
470 + // /stats/devices/screensize
471 + sprintf( '/sites/%d/stats/devices/(?P<device_property>[\w]+)', Jetpack_Options::get_option( 'id' ) ),
472 + array(
473 + 'methods' => WP_REST_Server::READABLE,
474 + 'callback' => array( $this, 'get_devices_stats_time_series' ),
475 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
476 + )
477 + );
478 +
479 + // Rerun commercial classificiation.
480 + register_rest_route(
481 + static::$namespace,
482 + sprintf( '/sites/%d/commercial-classification', Jetpack_Options::get_option( 'id' ) ),
483 + array(
484 + 'methods' => WP_REST_Server::EDITABLE,
485 + 'callback' => array( $this, 'run_commercial_classification' ),
486 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
487 + )
488 + );
489 +
490 + // Purchases endpoint.
491 + register_rest_route(
492 + static::$namespace,
493 + sprintf( '/sites/%d/purchases', Jetpack_Options::get_option( 'id' ) ),
494 + array(
495 + 'methods' => WP_REST_Server::READABLE,
496 + 'callback' => array( $this, 'get_site_purchases' ),
497 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
498 + )
499 + );
500 +
501 + // Get Location stats.
502 + register_rest_route(
503 + static::$namespace,
504 + sprintf( '/sites/%d/stats/location-views/(?P<geo_mode>country|region|city)', Jetpack_Options::get_option( 'id' ) ),
505 + array(
506 + 'methods' => WP_REST_Server::READABLE,
507 + 'callback' => array( $this, 'get_location_stats' ),
508 + 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
509 + )
510 + );
511 + }
512 +
513 + /**
514 + * Only administrators or users with capability `view_stats` can access the API.
515 + *
516 + * @return bool|WP_Error True if a blog token was used to sign the request, WP_Error otherwise.
517 + */
518 + public function can_user_view_general_stats_callback() {
519 + if ( current_user_can( 'manage_options' ) || current_user_can( 'view_stats' ) ) {
520 + return true;
521 + }
522 +
523 + return $this->get_forbidden_error();
524 + }
525 +
526 + /**
527 + * Only administrators can read or change the Stats settings, because `roles` decides who else can view Stats.
528 + *
529 + * @return bool|WP_Error
530 + */
531 + public function can_user_manage_stats_settings_callback() {
532 + if ( current_user_can( 'manage_options' ) ) {
533 + return true;
534 + }
535 +
536 + return $this->get_forbidden_error();
537 + }
538 +
539 + /**
540 + * Only administrators or users with capability `activate_wordads` can access the API.
541 + */
542 + public function can_user_view_wordads_stats_callback() {
543 + // phpcs:ignore WordPress.WP.Capabilities.Unknown
544 + if ( current_user_can( 'manage_options' ) || current_user_can( 'activate_wordads' ) ) {
545 + return true;
546 + }
547 +
548 + return $this->get_forbidden_error();
549 + }
550 +
551 + /**
552 + * Stats resource endpoint.
553 + *
554 + * @param WP_REST_Request $req The request object.
555 + * @return array
556 + */
557 + public function get_stats_resource( $req ) {
558 + switch ( $req->get_param( 'resource' ) ) {
559 + case 'file-downloads':
560 + return $this->wpcom_stats->get_file_downloads( $req->get_params() );
561 +
562 + case 'video-plays':
563 + return $this->wpcom_stats->get_video_plays( $req->get_params() );
564 +
565 + case 'clicks':
566 + return $this->wpcom_stats->get_clicks( $req->get_params() );
567 +
568 + case 'search-terms':
569 + return $this->wpcom_stats->get_search_terms( $req->get_params() );
570 +
571 + case 'top-authors':
572 + return $this->wpcom_stats->get_top_authors( $req->get_params() );
573 +
574 + case 'country-views':
575 + return $this->wpcom_stats->get_views_by_country( $req->get_params() );
576 +
577 + case 'referrers':
578 + return $this->wpcom_stats->get_referrers( $req->get_params() );
579 +
580 + case 'top-posts':
581 + return $this->wpcom_stats->get_top_posts( $req->get_params() );
582 +
583 + case 'archives':
584 + return $this->wpcom_stats->get_archives( $req->get_params() );
585 +
586 + case 'publicize':
587 + return $this->wpcom_stats->get_publicize_followers( $req->get_params() );
588 +
589 + case 'followers':
590 + return $this->wpcom_stats->get_followers( $req->get_params() );
591 +
592 + case 'tags':
593 + return $this->wpcom_stats->get_tags( $req->get_params() );
594 +
595 + case 'visits':
596 + return $this->wpcom_stats->get_visits( $req->get_params() );
597 +
598 + case 'comments':
599 + return $this->wpcom_stats->get_top_comments( $req->get_params() );
600 +
601 + case 'comment-followers':
602 + return $this->wpcom_stats->get_comment_followers( $req->get_params() );
603 +
604 + case 'streak':
605 + return $this->wpcom_stats->get_streak( $req->get_params() );
606 +
607 + case 'insights':
608 + return $this->wpcom_stats->get_insights( $req->get_params() );
609 +
610 + case 'highlights':
611 + return $this->wpcom_stats->get_highlights( $req->get_params() );
612 +
613 + case 'subscribers':
614 + return WPCOM_Client::request_as_blog_cached(
615 + sprintf(
616 + '/sites/%d/stats/subscribers?%s',
617 + Jetpack_Options::get_option( 'id' ),
618 + $this->filter_and_build_query_string(
619 + $req->get_query_params()
620 + )
621 + ),
622 + 'v1.1',
623 + array( 'timeout' => 5 )
624 + );
625 +
626 + default:
627 + return $this->get_forbidden_error();
628 + }
629 + }
630 +
631 + /**
632 + * Return likes of a single post.
633 + *
634 + * @param WP_REST_Request $req The request object.
635 + */
636 + public function get_single_post_likes( $req ) {
637 + $response = wp_remote_get(
638 + sprintf(
639 + '%s/rest/v1.2/sites/%d/posts/%d/likes?%s',
640 + Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
641 + Jetpack_Options::get_option( 'id' ),
642 + $req->get_param( 'resource_id' ),
643 + $this->filter_and_build_query_string(
644 + $req->get_params(),
645 + array( 'resource_id' )
646 + )
647 + ),
648 + array( 'timeout' => 5 )
649 + );
650 +
651 + $response_code = wp_remote_retrieve_response_code( $response );
652 + $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
653 +
654 + if ( is_wp_error( $response ) ) {
655 + return $response;
656 + }
657 +
658 + if ( 200 !== $response_code ) {
659 + return new WP_Error(
660 + isset( $response_body['error'] ) ? 'remote-error-' . $response_body['error'] : 'remote-error',
661 + $response_body['message'] ?? 'unknown remote error',
662 + array( 'status' => $response_code )
663 + );
664 + }
665 +
666 + return $response_body;
667 + }
668 +
669 + /**
670 + * Site Stats Resource endpoint.
671 + *
672 + * @param WP_REST_Request $req The request object.
673 + * @return array
674 + */
675 + public function get_single_resource_stats( $req ) {
676 + switch ( $req->get_param( 'resource' ) ) {
677 + case 'post':
678 + return $this->wpcom_stats->get_post_views(
679 + intval( $req->get_param( 'resource_id' ) ),
680 + $req->get_params()
681 + );
682 +
683 + case 'video':
684 + return $this->wpcom_stats->get_video_details(
685 + intval( $req->get_param( 'resource_id' ) ),
686 + $req->get_params()
687 + );
688 +
689 + default:
690 + return $this->get_forbidden_error();
691 + }
692 + }
693 +
694 + /**
695 + * Get brief information for a single post.
696 + *
697 + * @param WP_REST_Request $req The request object.
698 + * @return array
699 + */
700 + public function get_single_post( $req ) {
701 + $post = get_post( intval( $req->get_param( 'resource_id' ) ), 'OBJECT', 'display' );
702 + if ( is_wp_error( $post ) || empty( $post ) ) {
703 + return $post;
704 + }
705 +
706 + // The endpoint should be as compatible as possible with `/sites/$site_id/posts/$post_id`.
707 + // The reason we are not forwarding the request is that `/sites/$site_id/posts/$post_id` might require user tokens for private posts/sites, which is not possible for users without a WordPress.com account.
708 + // 'like_count' is not included in the response because it's available through another endpoint `/sites/$site_id/posts/$post_id/likes`.
709 + return array(
710 + 'ID' => $post->ID,
711 + 'site_ID' => Jetpack_Options::get_option( 'id' ),
712 + 'title' => $post->post_title,
713 + 'URL' => get_permalink( $post->ID ),
714 + 'type' => $post->post_type,
715 + 'status' => $post->post_status,
716 + 'discussion' => array( 'comment_count' => intval( $post->comment_count ) ),
717 + 'date' => $post->post_date,
718 + 'post_thumbnail' => array( 'URL' => get_the_post_thumbnail_url( $post->ID ) ),
719 + );
720 + }
721 +
722 + /**
723 + * Get site stats.
724 + *
725 + * @param WP_REST_Request $req The request object.
726 + * @return array
727 + */
728 + public function get_site_stats( $req ) {
729 + return $this->wpcom_stats->get_stats( $req->get_params() );
730 + }
731 +
732 + /**
733 + * List posts for the site.
734 + *
735 + * @param WP_REST_Request $req The request object.
736 + * @return array
737 + */
738 + public function get_site_posts( $req ) {
739 + // Force wpcom response.
740 + $params = array_merge( array( 'force' => 'wpcom' ), $req->get_params() );
741 + $response = wp_remote_get(
742 + sprintf(
743 + '%s/rest/v1.1/sites/%d/posts?%s',
744 + Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
745 + Jetpack_Options::get_option( 'id' ),
746 + $req->get_param( 'resource_id' ),
747 + $this->filter_and_build_query_string( $params, array( 'resource_id' ) )
748 + ),
749 + array( 'timeout' => 5 )
750 + );
751 +
752 + $response_code = wp_remote_retrieve_response_code( $response );
753 + $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
754 +
755 + if ( is_wp_error( $response ) ) {
756 + return $response;
757 + }
758 +
759 + if ( 200 !== $response_code ) {
760 + return new WP_Error(
761 + isset( $response_body['error'] ) ? 'remote-error-' . $response_body['error'] : 'remote-error',
762 + $response_body['message'] ?? 'unknown remote error',
763 + array( 'status' => $response_code )
764 + );
765 + }
766 +
767 + return $response_body;
768 + }
769 +
770 + /**
771 + * Get site subscribers counts.
772 + *
773 + * @param WP_REST_Request $req The request object.
774 + *
775 + * @return array
776 + */
777 + public function get_site_subscribers_counts( $req ) {
778 + return WPCOM_Client::request_as_blog_cached(
779 + sprintf(
780 + '/sites/%d/subscribers/counts?%s',
781 + Jetpack_Options::get_option( 'id' ),
782 + $this->filter_and_build_query_string(
783 + $req->get_query_params()
784 + )
785 + ),
786 + 'v2',
787 + array( 'timeout' => 5 ),
788 + null,
789 + 'wpcom'
790 + );
791 + }
792 +
793 + /**
794 + * Get site plan usage.
795 + *
796 + * @param WP_REST_Request $req The request object.
797 + *
798 + * @return array
799 + */
800 + public function get_site_plan_usage( $req ) {
801 + return WPCOM_Client::request_as_blog_cached(
802 + sprintf(
803 + '/sites/%d/jetpack-stats/usage?%s',
804 + Jetpack_Options::get_option( 'id' ),
805 + $this->filter_and_build_query_string(
806 + $req->get_query_params()
807 + )
808 + ),
809 + 'v2',
810 + array( 'timeout' => 5 ),
811 + null,
812 + 'wpcom',
813 + false
814 + );
815 + }
816 +
817 + /**
818 + * Get the Stats settings and the site's roles.
819 + *
820 + * @return array
821 + */
822 + public function get_stats_settings() {
823 + return $this->get_stats_settings_response();
824 + }
825 +
826 + /**
827 + * Save the Stats settings in the request.
828 + *
829 + * @param WP_REST_Request $req The request object.
830 + *
831 + * @return array|WP_Error The settings after the save, or why the values were refused.
832 + */
833 + public function update_stats_settings( $req ) {
834 + $params = $req->get_params();
835 + $keys = self::get_stats_settings_keys();
836 +
837 + $stats_values = array_intersect_key( $params, array_flip( $keys ) );
838 + if ( empty( $stats_values ) && ! isset( $params['wpcom_reader_views_enabled'] ) ) {
839 + return new WP_Error(
840 + 'jetpack_stats_missing_setting_field',
841 + sprintf(
842 + /* translators: %s: comma-separated list of the settings that can be changed. */
843 + __( 'Provide at least one of: %s.', 'jetpack-stats-admin' ),
844 + implode( ', ', array_merge( $keys, array( 'wpcom_reader_views_enabled' ) ) )
845 + ),
846 + array( 'status' => 400 )
847 + );
848 + }
849 +
850 + if ( ! empty( $stats_values ) ) {
851 + $result = Stats_Settings::update( $stats_values, $keys );
852 + if ( is_wp_error( $result ) ) {
853 + $result->add_data( array( 'status' => 400 ) );
854 + return $result;
855 + }
856 + }
857 +
858 + if ( isset( $params['wpcom_reader_views_enabled'] ) ) {
859 + $reader_views = (int) $params['wpcom_reader_views_enabled'];
860 + update_option( 'wpcom_reader_views_enabled', $reader_views );
861 + // update_option() also returns false for an unchanged value, so read the option back.
862 + if ( (int) get_option( 'wpcom_reader_views_enabled', 1 ) !== $reader_views ) {
863 + return new WP_Error(
864 + 'jetpack_stats_save_failed',
865 + __( 'The Stats settings could not be saved.', 'jetpack-stats-admin' ),
866 + array( 'status' => 400 )
867 + );
868 + }
869 + }
870 +
871 + return $this->get_stats_settings_response();
872 + }
873 +
874 + /**
875 + * The Stats settings the Settings screen offers.
876 + *
877 + * @return string[]
878 + */
879 + private static function get_stats_settings_keys() {
880 + // Nothing reads `do_not_track`, so the screen does not offer it.
881 + return array_values( array_diff( Stats_Settings::KEYS, array( 'do_not_track' ) ) );
882 + }
883 +
884 + /**
885 + * Build the settings response: the current values and the roles the toggles list.
886 + *
887 + * @return array
888 + */
889 + private function get_stats_settings_response() {
890 + if ( ! function_exists( 'get_editable_roles' ) ) {
891 + require_once ABSPATH . 'wp-admin/includes/user.php';
892 + }
893 +
894 + $roles = array();
895 + foreach ( get_editable_roles() as $slug => $role ) {
896 + $roles[] = array(
897 + 'slug' => $slug,
898 + 'name' => translate_user_role( $role['name'] ),
899 + );
900 + }
901 +
902 + return array(
903 + 'settings' => array_merge(
904 + Stats_Settings::get( self::get_stats_settings_keys() ),
905 + array( 'wpcom_reader_views_enabled' => (bool) get_option( 'wpcom_reader_views_enabled', true ) )
906 + ),
907 + 'roles' => $roles,
908 + );
909 + }
910 +
911 + /**
912 + * Post user feedback for Jetpack Stats.
913 + *
914 + * @param WP_REST_Request $req The request object.
915 + *
916 + * @return array
917 + */
918 + public function post_user_feedback( $req ) {
919 + $current_user = wp_get_current_user();
920 + $body_from_req = json_decode( $req->get_body(), true );
921 + $body_data = is_array( $body_from_req ) ? $body_from_req : array();
922 + $user_email = $current_user->user_email;
923 +
924 + return WPCOM_Client::request_as_blog_cached(
925 + sprintf(
926 + '/sites/%d/jetpack-stats/user-feedback?%s',
927 + Jetpack_Options::get_option( 'id' ),
928 + $this->filter_and_build_query_string(
929 + $req->get_query_params()
930 + )
931 + ),
932 + 'v2',
933 + array(
934 + 'timeout' => 5,
935 + 'method' => 'POST',
936 + 'headers' => array( 'Content-Type' => 'application/json' ),
937 + ),
938 + wp_json_encode(
939 + array_merge(
940 + $body_data,
941 + array(
942 + 'user_email' => $user_email,
943 + )
944 + ),
945 + JSON_UNESCAPED_SLASHES
946 + ),
947 + 'wpcom'
948 + );
949 + }
950 +
951 + /**
952 + * Whether site has never published post.
953 + *
954 + * @param WP_REST_Request $req The request object.
955 + * @return array
956 + */
957 + public function site_has_never_published_post( $req ) {
958 + return WPCOM_Client::request_as_blog_cached(
959 + sprintf(
960 + '/sites/%d/site-has-never-published-post?%s',
961 + Jetpack_Options::get_option( 'id' ),
962 + $this->filter_and_build_query_string(
963 + $req->get_params()
964 + )
965 + ),
966 + 'v2',
967 + array( 'timeout' => 5 ),
968 + null,
969 + 'wpcom'
970 + );
971 + }
972 +
973 + /**
974 + * Get detailed WordAds earnings information for the site.
975 + *
976 + * @param WP_REST_Request $req The request object.
977 + * @return array
978 + */
979 + public function get_wordads_earnings( $req ) {
980 + return WPCOM_Client::request_as_blog_cached(
981 + sprintf(
982 + '/sites/%d/wordads/earnings?%s',
983 + Jetpack_Options::get_option( 'id' ),
984 + $this->filter_and_build_query_string(
985 + $req->get_params()
986 + )
987 + ),
988 + 'v1.1',
989 + array( 'timeout' => 5 )
990 + );
991 + }
992 +
993 + /**
994 + * Get WordAds stats for the site.
995 + *
996 + * @param WP_REST_Request $req The request object.
997 + * @return array
998 + */
999 + public function get_wordads_stats( $req ) {
1000 + return WPCOM_Client::request_as_blog_cached(
1001 + sprintf(
1002 + '/sites/%d/wordads/stats?%s',
1003 + Jetpack_Options::get_option( 'id' ),
1004 + $this->filter_and_build_query_string(
1005 + $req->get_params()
1006 + )
1007 + ),
1008 + 'v1.1',
1009 + array( 'timeout' => 5 )
1010 + );
1011 + }
1012 +
1013 + /**
1014 + * Get Email stats as a list.
1015 + *
1016 + * @param WP_REST_Request $req The request object.
1017 + * @return array
1018 + */
1019 + public function get_email_stats_list( $req ) {
1020 + switch ( $req->get_param( 'resource' ) ) {
1021 + case 'summary':
1022 + return WPCOM_Client::request_as_blog_cached(
1023 + sprintf(
1024 + '/sites/%d/stats/emails/%s?%s',
1025 + Jetpack_Options::get_option( 'id' ),
1026 + $req->get_param( 'resource' ),
1027 + $this->filter_and_build_query_string(
1028 + $req->get_params()
1029 + )
1030 + ),
1031 + 'v1.1',
1032 + array( 'timeout' => 5 )
1033 + );
1034 + default:
1035 + return $this->get_forbidden_error();
1036 + }
1037 + }
1038 +
1039 + /**
1040 + * Get Email opens stats for a single post.
1041 + *
1042 + * @param WP_REST_Request $req The request object.
1043 + * @return array
1044 + */
1045 + public function get_email_opens_stats_single( $req ) {
1046 + switch ( $req->get_param( 'resource' ) ) {
1047 + case 'client':
1048 + case 'device':
1049 + case 'country':
1050 + case 'rate':
1051 + return WPCOM_Client::request_as_blog_cached(
1052 + sprintf(
1053 + '/sites/%d/stats/opens/emails/%d/%s?%s',
1054 + Jetpack_Options::get_option( 'id' ),
1055 + $req->get_param( 'post_id' ),
1056 + $req->get_param( 'resource' ),
1057 + $this->filter_and_build_query_string(
1058 + $req->get_params()
1059 + )
1060 + ),
1061 + 'v1.1',
1062 + array( 'timeout' => 5 )
1063 + );
1064 + default:
1065 + return $this->get_forbidden_error();
1066 + }
1067 + }
1068 +
1069 + /**
1070 + * Get Email clicks stats for a single post.
1071 + *
1072 + * @param WP_REST_Request $req The request object.
1073 + * @return array
1074 + */
1075 + public function get_email_clicks_stats_single( $req ) {
1076 + switch ( $req->get_param( 'resource' ) ) {
1077 + case 'client':
1078 + case 'device':
1079 + case 'country':
1080 + case 'rate':
1081 + case 'link':
1082 + case 'user-content-link':
1083 + return WPCOM_Client::request_as_blog_cached(
1084 + sprintf(
1085 + '/sites/%d/stats/clicks/emails/%d/%s?%s',
1086 + Jetpack_Options::get_option( 'id' ),
1087 + $req->get_param( 'post_id' ),
1088 + $req->get_param( 'resource' ),
1089 + $this->filter_and_build_query_string(
1090 + $req->get_params()
1091 + )
1092 + ),
1093 + 'v1.1',
1094 + array( 'timeout' => 5 )
1095 + );
1096 + default:
1097 + return $this->get_forbidden_error();
1098 + }
1099 + }
1100 +
1101 + /**
1102 + * Get Email stats time series.
1103 + *
1104 + * @param WP_REST_Request $req The request object.
1105 + * @return array
1106 + */
1107 + public function get_email_stats_time_series( $req ) {
1108 + switch ( $req->get_param( 'resource' ) ) {
1109 + case 'opens':
1110 + case 'clicks':
1111 + return WPCOM_Client::request_as_blog_cached(
1112 + sprintf(
1113 + '/sites/%d/stats/%s/emails/%d?%s',
1114 + Jetpack_Options::get_option( 'id' ),
1115 + $req->get_param( 'resource' ),
1116 + $req->get_param( 'post_id' ),
1117 + $this->filter_and_build_query_string(
1118 + $req->get_params()
1119 + )
1120 + ),
1121 + 'v1.1',
1122 + array( 'timeout' => 5 )
1123 + );
1124 + default:
1125 + return $this->get_forbidden_error();
1126 + }
1127 + }
1128 +
1129 + /**
1130 + * Get UTM stats time series.
1131 + *
1132 + * @param WP_REST_Request $req The request object.
1133 + * @return array
1134 + */
1135 + public function get_utm_stats_time_series( $req ) {
1136 + return WPCOM_Client::request_as_blog_cached(
1137 + sprintf(
1138 + '/sites/%d/stats/utm/%s?%s',
1139 + Jetpack_Options::get_option( 'id' ),
1140 + $req->get_param( 'utm_params' ),
1141 + $this->filter_and_build_query_string(
1142 + $req->get_params()
1143 + )
1144 + ),
1145 + 'v1.1',
1146 + array( 'timeout' => 10 )
1147 + );
1148 + }
1149 +
1150 + /**
1151 + * Get Devices stats time series.
1152 + *
1153 + * @param WP_REST_Request $req The request object.
1154 + * @return array
1155 + */
1156 + public function get_devices_stats_time_series( $req ) {
1157 + return WPCOM_Client::request_as_blog_cached(
1158 + sprintf(
1159 + '/sites/%d/stats/devices/%s?%s',
1160 + Jetpack_Options::get_option( 'id' ),
1161 + $req->get_param( 'device_property' ),
1162 + $this->filter_and_build_query_string(
1163 + $req->get_params()
1164 + )
1165 + ),
1166 + 'v1.1',
1167 + array( 'timeout' => 10 )
1168 + );
1169 + }
1170 +
1171 + /**
1172 + * Get Location stats.
1173 + *
1174 + * @param WP_REST_Request $req The request object.
1175 + * @return array
1176 + */
1177 + public function get_location_stats( $req ) {
1178 + $params = $req->get_params();
1179 + $geo_mode = $params['geo_mode'];
1180 + unset( $params['geo_mode'] );
1181 +
1182 + return $this->wpcom_stats->get_views_by_location( $geo_mode, $params );
1183 + }
1184 +
1185 + /**
1186 + * Dismiss or delay stats notices.
1187 + *
1188 + * @param WP_REST_Request $req The request object.
1189 + * @return array
1190 + */
1191 + public function update_notice_status( $req ) {
1192 + return ( new Notices() )->update_notice( $req->get_param( 'id' ), $req->get_param( 'status' ), $req->get_param( 'postponed_for' ) );
1193 + }
1194 +
1195 + /**
1196 + * Get stats notices.
1197 + *
1198 + * @param WP_REST_Request $req The request object.
1199 + * @return array
1200 + */
1201 + public function get_notice_status( $req ) {
1202 + return ( new Notices() )->get_notices_to_show( (bool) $req->get_param( 'include_details' ) );
1203 + }
1204 +
1205 + /**
1206 + * Get the list of spam referrers.
1207 + *
1208 + * @return array
1209 + */
1210 + public function get_referrer_spam_list() {
1211 + return WPCOM_Client::request_as_blog(
1212 + sprintf(
1213 + '/sites/%d/stats/referrers/spam',
1214 + Jetpack_Options::get_option( 'id' )
1215 + ),
1216 + 'v1.1',
1217 + array(
1218 + 'timeout' => 5,
1219 + 'method' => 'GET',
1220 + )
1221 + );
1222 + }
1223 +
1224 + /**
1225 + * Mark a referrer as spam.
1226 + *
1227 + * @param WP_REST_Request $req The request object.
1228 + * @return array
1229 + */
1230 + public function mark_referrer_spam( $req ) {
1231 + return WPCOM_Client::request_as_blog(
1232 + sprintf(
1233 + '/sites/%d/stats/referrers/spam/new?%s',
1234 + Jetpack_Options::get_option( 'id' ),
1235 + $this->filter_and_build_query_string(
1236 + $req->get_query_params()
1237 + )
1238 + ),
1239 + 'v1.1',
1240 + array(
1241 + 'timeout' => 5,
1242 + 'method' => 'POST',
1243 + )
1244 + );
1245 + }
1246 +
1247 + /**
1248 + * Unmark a referrer as spam.
1249 + *
1250 + * @param WP_REST_Request $req The request object.
1251 + * @return array
1252 + */
1253 + public function unmark_referrer_spam( $req ) {
1254 + return WPCOM_Client::request_as_blog(
1255 + sprintf(
1256 + '/sites/%d/stats/referrers/spam/delete?%s',
1257 + Jetpack_Options::get_option( 'id' ),
1258 + $this->filter_and_build_query_string(
1259 + $req->get_query_params()
1260 + )
1261 + ),
1262 + 'v1.1',
1263 + array(
1264 + 'timeout' => 5,
1265 + 'method' => 'POST',
1266 + )
1267 + );
1268 + }
1269 +
1270 + /**
1271 + * Toggle modules on dashboard.
1272 + *
1273 + * @param WP_REST_Request $req The request object.
1274 + * @return array
1275 + */
1276 + public function update_dashboard_modules( $req ) {
1277 + // Clear dashboard modules cache.
1278 + delete_transient( static::JETPACK_STATS_DASHBOARD_MODULES_CACHE_KEY );
1279 + return WPCOM_Client::request_as_blog(
1280 + sprintf(
1281 + '/sites/%d/jetpack-stats-dashboard/modules?%s',
1282 + Jetpack_Options::get_option( 'id' ),
1283 + $this->filter_and_build_query_string(
1284 + $req->get_query_params()
1285 + )
1286 + ),
1287 + 'v2',
1288 + array(
1289 + 'timeout' => 5,
1290 + 'method' => 'POST',
1291 + 'headers' => array( 'Content-Type' => 'application/json' ),
1292 + ),
1293 + $req->get_body(),
1294 + 'wpcom'
1295 + );
1296 + }
1297 +
1298 + /**
1299 + * Get modules on dashboard.
1300 + *
1301 + * @param WP_REST_Request $req The request object.
1302 + * @return array
1303 + */
1304 + public function get_dashboard_modules( $req ) {
1305 + return WPCOM_Client::request_as_blog_cached(
1306 + sprintf(
1307 + '/sites/%d/jetpack-stats-dashboard/modules?%s',
1308 + Jetpack_Options::get_option( 'id' ),
1309 + $this->filter_and_build_query_string(
1310 + $req->get_query_params()
1311 + )
1312 + ),
1313 + 'v2',
1314 + array(
1315 + 'timeout' => 5,
1316 + ),
1317 + null,
1318 + 'wpcom',
1319 + true,
1320 + static::JETPACK_STATS_DASHBOARD_MODULES_CACHE_KEY
1321 + );
1322 + }
1323 +
1324 + /**
1325 + * Update module settings on dashboard.
1326 + *
1327 + * @param WP_REST_Request $req The request object.
1328 + * @return array
1329 + */
1330 + public function update_dashboard_module_settings( $req ) {
1331 + // Clear dashboard modules cache.
1332 + delete_transient( static::JETPACK_STATS_DASHBOARD_MODULE_SETTINGS_CACHE_KEY );
1333 + return WPCOM_Client::request_as_blog(
1334 + sprintf(
1335 + '/sites/%d/jetpack-stats-dashboard/module-settings?%s',
1336 + Jetpack_Options::get_option( 'id' ),
1337 + $this->filter_and_build_query_string(
1338 + $req->get_query_params()
1339 + )
1340 + ),
1341 + 'v2',
1342 + array(
1343 + 'timeout' => 5,
1344 + 'method' => 'POST',
1345 + 'headers' => array( 'Content-Type' => 'application/json' ),
1346 + ),
1347 + $req->get_body(),
1348 + 'wpcom'
1349 + );
1350 + }
1351 +
1352 + /**
1353 + * Get module settings on dashboard.
1354 + *
1355 + * @param WP_REST_Request $req The request object.
1356 + * @return array
1357 + */
1358 + public function get_dashboard_module_settings( $req ) {
1359 + return WPCOM_Client::request_as_blog_cached(
1360 + sprintf(
1361 + '/sites/%d/jetpack-stats-dashboard/module-settings?%s',
1362 + Jetpack_Options::get_option( 'id' ),
1363 + $this->filter_and_build_query_string(
1364 + $req->get_query_params()
1365 + )
1366 + ),
1367 + 'v2',
1368 + array(
1369 + 'timeout' => 5,
1370 + ),
1371 + null,
1372 + 'wpcom',
1373 + true,
1374 + static::JETPACK_STATS_DASHBOARD_MODULE_SETTINGS_CACHE_KEY
1375 + );
1376 + }
1377 +
1378 + /**
1379 + * Run commercial classification.
1380 + *
1381 + * @param WP_REST_Request $req The request object.
1382 + * @return array
1383 + */
1384 + public function run_commercial_classification( $req ) {
1385 + return WPCOM_Client::request_as_blog(
1386 + sprintf(
1387 + '/sites/%d/commercial-classification?%s',
1388 + Jetpack_Options::get_option( 'id' ),
1389 + $this->filter_and_build_query_string(
1390 + $req->get_query_params()
1391 + )
1392 + ),
1393 + 'v2',
1394 + array(
1395 + 'timeout' => 5,
1396 + 'method' => 'POST',
1397 + ),
1398 + null,
1399 + 'wpcom'
1400 + );
1401 + }
1402 +
1403 + /**
1404 + * Get purchases array; I don't see anything sensetive in there, so didn't sentinizie it.
1405 + * Plus it is the same case as Jetpack.
1406 + *
1407 + * @param WP_REST_Request $req The request object.
1408 + * @return array
1409 + */
1410 + public function get_site_purchases( $req ) {
1411 + return WPCOM_Client::request_as_blog_cached(
1412 + sprintf(
1413 + '/upgrades?site=%d&%s',
1414 + Jetpack_Options::get_option( 'id' ),
1415 + $this->filter_and_build_query_string(
1416 + $req->get_query_params()
1417 + )
1418 + ),
1419 + 'v1.2',
1420 + array( 'timeout' => 10 ),
1421 + null,
1422 + 'rest',
1423 + false
1424 + );
1425 + }
1426 +
1427 + /**
1428 + * Return a WP_Error object with a forbidden error.
1429 + */
1430 + protected function get_forbidden_error() {
1431 + $error_msg = esc_html__(
1432 + 'You are not allowed to perform this action.',
1433 + 'jetpack-stats-admin'
1434 + );
1435 +
1436 + return new WP_Error( 'rest_forbidden', $error_msg, array( 'status' => rest_authorization_required_code() ) );
1437 + }
1438 +
1439 + /**
1440 + * Filter and build query string from all the requested params.
1441 + *
1442 + * @param array $params The params to filter.
1443 + * @param array $keys_to_unset The keys to unset from the params array.
1444 + * @return string The filtered and built query string.
1445 + */
1446 + protected function filter_and_build_query_string( $params, $keys_to_unset = array() ) {
1447 + if ( isset( $params['rest_route'] ) ) {
1448 + unset( $params['rest_route'] );
1449 + }
1450 + if ( ! empty( $keys_to_unset ) && is_array( $keys_to_unset ) ) {
1451 + foreach ( $keys_to_unset as $key ) {
1452 + if ( isset( $params[ $key ] ) ) {
1453 + unset( $params[ $key ] );
1454 + }
1455 + }
1456 + }
1457 + return http_build_query( $params );
1458 + }
1459 +}