PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 13.8.3 All 506 releases
← All changes | jetpack_vendor/automattic/jetpack-stats-admin/src/class-rest-controller.php +630 -11 12.3.2 → 16.3-a.7 View file →
@@ -8,11 +8,13 @@
8 8
9 9 namespace Automattic\Jetpack\Stats_Admin;
10 10
11 11 use Automattic\Jetpack\Constants;
12 +use Automattic\Jetpack\Stats\Settings as Stats_Settings;
12 13 use Automattic\Jetpack\Stats\WPCOM_Stats;
13 14 use Jetpack_Options;
14 15 use WP_Error;
16 +use WP_REST_Request;
15 17 use WP_REST_Server;
16 18
17 19 /**
18 20 * Registers the REST routes for Stats.
@@ -43,8 +45,21 @@
43 45 $this->wpcom_stats = new WPCOM_Stats();
44 46 }
45 47
46 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 + /**
47 62 * Registers the REST routes for Odyssey Stats.
48 63 *
49 64 * Odyssey Stats is built from `wp-calypso`, which leverages the `public-api.wordpress.com` API.
50 65 * The current Site ID is added as part of the route, so that the front end doesn't have to handle the differences.
@@ -140,8 +155,69 @@
140 155 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
141 156 )
142 157 );
143 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 +
144 220 // WordAds Earnings.
145 221 register_rest_route(
146 222 static::$namespace,
147 223 sprintf( '/sites/%d/wordads/earnings', Jetpack_Options::get_option( 'id' ) ),
@@ -184,9 +260,9 @@
184 260 'description' => 'Status of the notice',
185 261 ),
186 262 'postponed_for' => array(
187 263 'type' => 'number',
188 - 'default' => null,
264 + 'default' => 0,
189 265 'description' => 'Postponed for (in seconds)',
190 266 'minimum' => 0,
191 267 ),
192 268 ),
@@ -213,9 +289,10 @@
213 289 'description' => 'Status of the notice',
214 290 ),
215 291 'postponed_for' => array(
216 292 'type' => 'number',
217 - 'default' => null,
293 + // Forwarded to WPCOM as-is, whose schema rejects the null an omitted param would carry.
294 + 'default' => 0,
218 295 'description' => 'Postponed for (in seconds)',
219 296 'minimum' => 0,
220 297 ),
221 298 ),
@@ -229,11 +306,29 @@
229 306 array(
230 307 'methods' => WP_REST_Server::READABLE,
231 308 'callback' => array( $this, 'get_notice_status' ),
232 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 + ),
233 317 )
234 318 );
235 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 +
236 331 // Mark referrer spam.
237 332 register_rest_route(
238 333 static::$namespace,
239 334 sprintf( '/sites/%d/stats/referrers/spam/new', Jetpack_Options::get_option( 'id' ) ),
@@ -311,8 +406,109 @@
311 406 'callback' => array( $this, 'get_dashboard_module_settings' ),
312 407 'permission_callback' => array( $this, 'can_user_view_general_stats_callback' ),
313 408 )
314 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 + );
315 511 }
316 512
317 513 /**
318 514 * Only administrators or users with capability `view_stats` can access the API.
@@ -327,8 +523,21 @@
327 523 return $this->get_forbidden_error();
328 524 }
329 525
330 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 + /**
331 540 * Only administrators or users with capability `activate_wordads` can access the API.
332 541 */
333 542 public function can_user_view_wordads_stats_callback() {
334 543 // phpcs:ignore WordPress.WP.Capabilities.Unknown
@@ -370,8 +579,11 @@
370 579
371 580 case 'top-posts':
372 581 return $this->wpcom_stats->get_top_posts( $req->get_params() );
373 582
583 + case 'archives':
584 + return $this->wpcom_stats->get_archives( $req->get_params() );
585 +
374 586 case 'publicize':
375 587 return $this->wpcom_stats->get_publicize_followers( $req->get_params() );
376 588
377 589 case 'followers':
@@ -445,9 +657,9 @@
445 657
446 658 if ( 200 !== $response_code ) {
447 659 return new WP_Error(
448 660 isset( $response_body['error'] ) ? 'remote-error-' . $response_body['error'] : 'remote-error',
449 - isset( $response_body['message'] ) ? $response_body['message'] : 'unknown remote error',
661 + $response_body['message'] ?? 'unknown remote error',
450 662 array( 'status' => $response_code )
451 663 );
452 664 }
453 665
@@ -490,13 +702,21 @@
490 702 if ( is_wp_error( $post ) || empty( $post ) ) {
491 703 return $post;
492 704 }
493 705
494 - // It shouldn't be a problem because only title and ID are exposed.
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`.
495 709 return array(
496 - 'ID' => $post->ID,
497 - 'title' => $post->post_title,
498 - 'URL' => get_permalink( $post->ID ),
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 ) ),
499 719 );
500 720 }
501 721
502 722 /**
@@ -519,9 +739,9 @@
519 739 // Force wpcom response.
520 740 $params = array_merge( array( 'force' => 'wpcom' ), $req->get_params() );
521 741 $response = wp_remote_get(
522 742 sprintf(
523 - '%s/rest/v1.2/sites/%d/posts?%s',
743 + '%s/rest/v1.1/sites/%d/posts?%s',
524 744 Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
525 745 Jetpack_Options::get_option( 'id' ),
526 746 $req->get_param( 'resource_id' ),
527 747 $this->filter_and_build_query_string( $params, array( 'resource_id' ) )
@@ -538,9 +758,9 @@
538 758
539 759 if ( 200 !== $response_code ) {
540 760 return new WP_Error(
541 761 isset( $response_body['error'] ) ? 'remote-error-' . $response_body['error'] : 'remote-error',
542 - isset( $response_body['message'] ) ? $response_body['message'] : 'unknown remote error',
762 + $response_body['message'] ?? 'unknown remote error',
543 763 array( 'status' => $response_code )
544 764 );
545 765 }
546 766
@@ -570,8 +790,166 @@
570 790 );
571 791 }
572 792
573 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 + /**
574 952 * Whether site has never published post.
575 953 *
576 954 * @param WP_REST_Request $req The request object.
577 955 * @return array
@@ -632,8 +1010,180 @@
632 1010 );
633 1011 }
634 1012
635 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 + /**
636 1186 * Dismiss or delay stats notices.
637 1187 *
638 1188 * @param WP_REST_Request $req The request object.
639 1189 * @return array
@@ -644,15 +1194,35 @@
644 1194
645 1195 /**
646 1196 * Get stats notices.
647 1197 *
1198 + * @param WP_REST_Request $req The request object.
648 1199 * @return array
649 1200 */
650 - public function get_notice_status() {
651 - return ( new Notices() )->get_notices_to_show();
1201 + public function get_notice_status( $req ) {
1202 + return ( new Notices() )->get_notices_to_show( (bool) $req->get_param( 'include_details' ) );
652 1203 }
653 1204
654 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 + /**
655 1225 * Mark a referrer as spam.
656 1226 *
657 1227 * @param WP_REST_Request $req The request object.
658 1228 * @return array
@@ -801,8 +1371,57 @@
801 1371 null,
802 1372 'wpcom',
803 1373 true,
804 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
805 1424 );
806 1425 }
807 1426
808 1427 /**