PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
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 13.9.2 14.0.1 All 504 releases
← All changes | modules/stats.php +257 -537 12.0.3 → 16.3-a.1 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 3 * Module Name: Jetpack Stats
4 - * Module Description: Collect valuable traffic stats and insights.
4 + * Module Description: Clear, concise traffic insights right in your WordPress dashboard.
5 5 * Sort Order: 1
6 6 * Recommendation Order: 2
7 7 * First Introduced: 1.1
8 8 * Requires Connection: Yes
@@ -13,22 +13,26 @@
13 13 *
14 14 * @package automattic/jetpack
15 15 */
16 16
17 +use Automattic\Jetpack\Admin_UI\Admin_Menu;
17 18 use Automattic\Jetpack\Connection\Client;
18 -use Automattic\Jetpack\Connection\Manager as Connection_Manager;
19 -use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
20 19 use Automattic\Jetpack\Redirect;
21 20 use Automattic\Jetpack\Stats\Main as Stats;
22 21 use Automattic\Jetpack\Stats\Options as Stats_Options;
23 22 use Automattic\Jetpack\Stats\Tracking_Pixel as Stats_Tracking_Pixel;
23 +use Automattic\Jetpack\Stats\WPCOM_Stats;
24 24 use Automattic\Jetpack\Stats\XMLRPC_Provider as Stats_XMLRPC;
25 +use Automattic\Jetpack\Stats_Admin\Admin_Post_List_Column;
25 26 use Automattic\Jetpack\Stats_Admin\Dashboard as Stats_Dashboard;
26 27 use Automattic\Jetpack\Stats_Admin\Main as Stats_Main;
27 -use Automattic\Jetpack\Stats_Admin\Notices as Stats_Notices;
28 28 use Automattic\Jetpack\Status\Host;
29 29 use Automattic\Jetpack\Tracking;
30 30
31 +if ( ! defined( 'ABSPATH' ) ) {
32 + exit( 0 );
33 +}
34 +
31 35 if ( defined( 'STATS_DASHBOARD_SERVER' ) ) {
32 36 return;
33 37 }
34 38
@@ -51,20 +55,21 @@
51 55 */
52 56 function stats_load() {
53 57 Jetpack::enable_module_configurable( __FILE__ );
54 58
55 - add_action( 'wp_head', 'stats_admin_bar_head', 100 );
59 + // Only run the callback for those who can see the stats.
60 + if ( is_user_logged_in() && current_user_can( 'view_stats' ) ) {
61 + add_action( 'admin_head', 'stats_admin_bar_head', 100 );
62 + add_action( 'wp_head', 'stats_admin_bar_head', 100 );
63 + }
56 64
65 + Admin_Post_List_Column::register();
66 +
57 67 add_action( 'jetpack_admin_menu', 'stats_admin_menu' );
68 + add_action( 'wp_before_admin_bar_render', 'stats_add_link_to_admin_bar_site_menu' );
58 69
59 70 add_filter( 'pre_option_db_version', 'stats_ignore_db_version' );
60 71
61 - // Add an icon to see stats in WordPress.com for a particular post.
62 - add_action( 'admin_print_styles-edit.php', 'jetpack_stats_load_admin_css' );
63 - add_filter( 'manage_posts_columns', 'jetpack_stats_post_table' );
64 - add_filter( 'manage_pages_columns', 'jetpack_stats_post_table' );
65 - add_action( 'manage_posts_custom_column', 'jetpack_stats_post_table_cell', 10, 2 );
66 - add_action( 'manage_pages_custom_column', 'jetpack_stats_post_table_cell', 10, 2 );
67 72 // Filter for adding the Jetpack plugin version to tracking stats.
68 73 add_filter( 'stats_array', 'filter_stats_array_add_jp_version' );
69 74
70 75 require_once __DIR__ . '/stats/class-jetpack-stats-upgrade-nudges.php';
@@ -134,9 +139,9 @@
134 139 * Stats Build View Data.
135 140 *
136 141 * @deprecated 11.5
137 142 * @access public
138 - * @return array.
143 + * @return array
139 144 */
140 145 function stats_build_view_data() {
141 146 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Tracking_Pixel::build_view_data' );
142 147 return Stats_Tracking_Pixel::build_view_data();
@@ -142,50 +147,14 @@
142 147 return Stats_Tracking_Pixel::build_view_data();
143 148 }
144 149
145 150 /**
146 - * Stats Footer.
147 - *
148 - * @deprecated 11.5
149 - * @access public
150 - * @return void
151 - */
152 -function stats_footer() {
153 - _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Tracking_Pixel::add_to_footer' );
154 - Stats_Tracking_Pixel::add_to_footer();
155 -}
156 -
157 -/**
158 - * Render the stats footer
159 - *
160 - * @deprecated 11.5
161 - *
162 - * @param array $data Array of data for the JS stats tracker.
163 - */
164 -function stats_render_footer( $data ) {
165 - _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Tracking_Pixel::render_footer' );
166 - Stats_Tracking_Pixel::render_footer( $data );
167 -}
168 -
169 -/**
170 - * Render the stats footer for AMP output.
171 - *
172 - * @deprecated 11.5
173 - *
174 - * @param array $data Array of data for the AMP pixel tracker.
175 - */
176 -function stats_render_amp_footer( $data ) {
177 - _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Tracking_Pixel::render_amp_footer' );
178 - Stats_Tracking_Pixel::render_amp_footer( $data );
179 -}
180 -
181 -/**
182 151 * Stats Get Options.
183 152 *
184 153 * @deprecated 11.5
185 154 *
186 155 * @access public
187 - * @return array.
156 + * @return array
188 157 */
189 158 function stats_get_options() {
190 159 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Options::get_options' );
191 160 return Stats_Options::get_options();
@@ -197,9 +166,9 @@
197 166 * @deprecated 11.5
198 167 *
199 168 * @access public
200 169 * @param mixed $option Option.
201 - * @return mixed|null.
170 + * @return mixed|null
202 171 */
203 172 function stats_get_option( $option ) {
204 173 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Options::get_option' );
205 174 return Stats_Options::get_option( $option );
@@ -212,9 +181,9 @@
212 181 *
213 182 * @access public
214 183 * @param mixed $option Option.
215 184 * @param mixed $value Value.
216 - * @return bool.
185 + * @return bool
217 186 */
218 187 function stats_set_option( $option, $value ) {
219 188 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Automattic\Jetpack\Stats\Options::set_option' );
220 189 return Stats_Options::set_option( $option, $value );
@@ -263,9 +232,9 @@
263 232 $redirect_url = str_replace( array( '/wp-admin/index.php?', '/wp-admin/?' ), '/wp-admin/admin.php?', isset( $_SERVER['REQUEST_URI'] ) ? filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : null );
264 233 $relative_pos = strpos( $redirect_url, '/wp-admin/' );
265 234 if ( false !== $relative_pos ) {
266 235 wp_safe_redirect( admin_url( substr( $redirect_url, $relative_pos + 10 ) ) );
267 - exit;
236 + exit( 0 );
268 237 }
269 238 }
270 239
271 240 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -275,20 +244,28 @@
275 244 Stats_Main::update_new_stats_status( true );
276 245 }
277 246
278 247 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
279 - if ( ( new Host() )->is_woa_site() || ! Stats_Options::get_option( 'enable_odyssey_stats' ) || isset( $_GET['noheader'] ) ) {
248 + if ( ! Stats_Options::get_option( 'enable_odyssey_stats' ) || isset( $_GET['noheader'] ) ) {
280 249 // Show old Jetpack Stats interface for:
281 - // - Atomic sites.
282 250 // - When the "enable_odyssey_stats" option is disabled.
283 251 // - When being shown in the adminbar outside of wp-admin.
284 - $hook = add_submenu_page( 'jetpack', __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'stats', 'jetpack_admin_ui_stats_report_page_wrapper' );
252 + $hook = Admin_Menu::add_menu(
253 + __( 'Stats', 'jetpack' ),
254 + __( 'Stats', 'jetpack' ),
255 + 'view_stats',
256 + 'stats',
257 + 'jetpack_admin_ui_stats_report_page_wrapper',
258 + null,
259 + array(
260 + 'product' => 'stats',
261 + 'key' => 'jetpack-stats',
262 + )
263 + );
285 264 add_action( "load-$hook", 'stats_reports_load' );
286 265 } else {
287 266 // Enable the new Odyssey Stats experience.
288 - $stats_dashboard = new Stats_Dashboard();
289 - $hook = add_submenu_page( 'jetpack', __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'stats', array( $stats_dashboard, 'render' ) );
290 - add_action( "load-$hook", array( $stats_dashboard, 'admin_init' ) );
267 + Stats_Dashboard::init();
291 268 }
292 269 }
293 270
294 271 /**
@@ -328,9 +305,8 @@
328 305 // Detect if JS is on. If so, remove cookie so next page load is via JS.
329 306 add_action( 'admin_print_footer_scripts', 'stats_js_remove_stnojs_cookie' );
330 307 } elseif ( ! isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
331 308 // Normal page load. Load page content via JS.
332 - add_action( 'admin_print_footer_scripts', 'stats_js_load_page_via_ajax' );
333 309 add_action( 'admin_print_footer_scripts', 'stats_script_dismiss_nudge_handler' );
334 310 }
335 311 }
336 312
@@ -349,10 +325,10 @@
349 325 element.classList.toggle( "is-hidden" );
350 326 // Send an AJAX request.
351 327 // Note we can provide a 'postponed_for' parameter to set the delay.
352 328 // Without a parameter it defaults to 30 days which is what we want here.
353 - let nonce = <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ) ); ?>;
354 - let url = <?php echo wp_json_encode( rest_url( '/jetpack/v4/stats-app/stats/notices' ) ); ?>;
329 + let nonce = <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
330 + let url = <?php echo wp_json_encode( rest_url( '/jetpack/v4/stats-app/stats/notices' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
355 331 let data = {
356 332 id: 'opt_in_new_stats',
357 333 status: 'postponed',
358 334 };
@@ -380,16 +356,8 @@
380 356 max-width: 1040px;
381 357 margin: 0 auto;
382 358 overflow: hidden;
383 359 }
384 -
385 -#stats-loading-wrap p {
386 - text-align: center;
387 - font-size: 2em;
388 - margin-bottom: 3em;
389 - height: 64px;
390 - line-height: 64px;
391 -}
392 360 </style>
393 361 <?php
394 362 }
395 363
@@ -403,9 +371,9 @@
403 371 $parsed = wp_parse_url( admin_url() );
404 372 ?>
405 373 <script type="text/javascript">
406 374 /* <![CDATA[ */
407 -document.cookie = 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=<?php echo esc_js( $parsed['path'] ); ?>';
375 +document.cookie = <?php echo wp_json_encode( 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=' . $parsed['path'], JSON_UNESCAPED_SLASHES | JSON_HEX_TAG ); ?>;
408 376 /* ]]> */
409 377 </script>
410 378 <?php
411 379 }
@@ -410,33 +378,8 @@
410 378 <?php
411 379 }
412 380
413 381 /**
414 - * Normal page load. Load page content via JS.
415 - *
416 - * @access public
417 - * @return void
418 - */
419 -function stats_js_load_page_via_ajax() {
420 - ?>
421 -<script type="text/javascript">
422 -/* <![CDATA[ */
423 -if ( -1 == document.location.href.indexOf( 'noheader' ) ) {
424 - jQuery( function( $ ) {
425 - const loadStatsUrl = new URL( document.location.href );
426 - loadStatsUrl.searchParams.append( 'noheader', 1 );
427 - $.get( loadStatsUrl.toString(), function( responseText ) {
428 - $( '#stats-loading-wrap' ).replaceWith( responseText );
429 - $( '#jp-stats-wrap' )[0].dispatchEvent( new Event( 'stats-loaded' ) );
430 - } );
431 - } );
432 -}
433 -/* ]]> */
434 -</script>
435 - <?php
436 -}
437 -
438 -/**
439 382 * Jetpack Admin Page Wrapper.
440 383 */
441 384 function jetpack_admin_ui_stats_report_page_wrapper() {
442 385 if ( ! isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) && empty( $_COOKIE['stnojs'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -453,19 +396,110 @@
453 396 * @param bool $main_chart_only (default: false) Main Chart Only.
454 397 */
455 398 function stats_reports_page( $main_chart_only = false ) {
456 399 if ( isset( $_GET['dashboard'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
457 - return stats_dashboard_widget_content();
400 + stats_dashboard_widget_content();
401 + exit( 0 ); // @phan-suppress-current-line PhanPluginUnreachableCode -- Safer to include it even though stats_dashboard_widget_content() never returns.
458 402 }
459 403
460 - $blog_id = Stats_Options::get_option( 'blog_id' );
404 + $blog_id = Stats_Options::get_option( 'blog_id' );
405 + $learn_url = Redirect::get_url( 'jetpack-stats-learn-more' );
406 + $redirect_url = admin_url( 'admin.php?page=stats&enable_new_stats=1' );
407 + $stats_bg_url = plugins_url( 'images/odyssey-upgrade/background.png', JETPACK__PLUGIN_FILE );
408 + $stats_bg_gradient_url = plugins_url( 'images/odyssey-upgrade/gradient.png', JETPACK__PLUGIN_FILE );
461 409
462 - if ( ! $main_chart_only && ! isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) && empty( $_COOKIE['stnojs'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
463 - $nojs_url = add_query_arg( 'nojs', '1' );
464 - $http = is_ssl() ? 'https' : 'http';
465 - // Loading message. No JS fallback message.
410 + if ( ! $main_chart_only && ! isset( $_GET['noheader'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
466 411 ?>
467 412
413 + <style>
414 + .stats-odyssey-notice {
415 + display: flex;
416 + font-size: var( --font-body );
417 +
418 + border: 1px solid var( --jp-gray-5 );
419 + border-left-color: var( --jp-black );
420 + border-left-width: 6px;
421 + border-radius: 4px;
422 +
423 + margin-top: 24px;
424 + background: white;
425 + position: relative;
426 + }
427 + .stats-odyssey-notice--content__highlighted {
428 + border-left-color: var( --jp-red );
429 + }
430 + .stats-odyssey-notice--content {
431 + padding: 24px 0 24px 30px;
432 + font-size: 2em;
433 + width: 100%;
434 + }
435 + .stats-odyssey-notice--content-header {
436 + font-size: 24px;
437 + line-height: 32px;
438 + margin: 0;
439 + margin-bottom: 8px;
440 + }
441 + .stats-odyssey-notice--content-text {
442 + font-size: 16px;
443 + margin: 0;
444 + }
445 + .stats-odyssey-notice--image-container {
446 + background-image: url("<?php echo esc_url( $stats_bg_url ); ?>"), url("<?php echo esc_url( $stats_bg_gradient_url ); ?>");
447 + background-size: cover;
448 + padding-right: 28px;
449 + width: 100%;
450 + }
451 + .stats-odyssey-notice--close-button {
452 + position: absolute;
453 + top: 1rem;
454 + right: 1rem;
455 + background-color: transparent;
456 + border: none;
457 + cursor: pointer;
458 + }
459 + .stats-odyssey-notice--action-bar {
460 + display: flex;
461 + align-items: center;
462 + margin-top: 24px;
463 + }
464 + .stats-odyssey-notice--primary-button {
465 + margin-right: 18px;
466 + padding-left: 20px;
467 + padding-right: 20px;
468 + font-size: 16px;
469 + border-color: black;
470 + background-color: black;
471 + }
472 + .stats-odyssey-notice--primary-button:hover {
473 + border-color: #3c434a;
474 + background-color: #3c434a;
475 + }
476 + .is-primary-link {
477 + color: white;
478 + text-decoration: none;
479 + }
480 + .is-primary-link:active {
481 + color: white;
482 + }
483 + .is-primary-link:focus {
484 + color: white;
485 + box-shadow: none;
486 + outline: none;
487 + }
488 + .is-primary-link:hover {
489 + color: white;
490 + }
491 + .is-secondary-link {
492 + color: black;
493 + font-size: var( --font-body );
494 + }
495 + .is-secondary-link:hover {
496 + color: black;
497 + }
498 + .is-hidden {
499 + display: none;
500 + }
501 + </style>
468 502 <div id="jp-stats-wrap">
469 503 <div class="wrap">
470 504 <h1><?php esc_html_e( 'Jetpack Stats', 'jetpack' ); ?>
471 505 <?php
@@ -473,9 +507,9 @@
473 507 $i18n_headers = jetpack_get_module_i18n( 'stats' );
474 508 ?>
475 509 <a
476 510 style="font-size:13px;"
477 - href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack#/settings?term=' . rawurlencode( $i18n_headers['name'] ) ) ); ?>"
511 + href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack#/settings?term=' . rawurlencode( $i18n_headers['name'] ?? '' ) ) ); ?>"
478 512 >
479 513 <?php esc_html_e( 'Configure', 'jetpack' ); ?>
480 514 </a>
481 515 <?php
@@ -480,27 +514,27 @@
480 514 </a>
481 515 <?php
482 516 endif;
483 517
484 - /**
485 - * Sets external resource URL.
486 - *
487 - * @module stats
488 - *
489 - * @since 1.4.0
490 - * @todo Clean up various uses of this filter. It's seemingly filtering different types of images in different places.
491 - *
492 - * @param string $args URL of external resource.
493 - */
494 - $static_url = apply_filters( 'jetpack_static_url', "{$http}://en.wordpress.com/i/loading/loading-64.gif" );
495 518 ?>
496 519 </h2>
497 520 </div>
498 - <div id="stats-loading-wrap" class="wrap">
499 - <p class="hide-if-no-js"><img width="32" height="32" alt="<?php esc_attr_e( 'Loading&hellip;', 'jetpack' ); ?>" src="<?php echo esc_url( $static_url ); ?>" /></p>
500 - <p class="hide-if-js"><?php esc_html_e( 'Jetpack Stats work better with JavaScript enabled.', 'jetpack' ); ?><br />
501 - <a href="<?php echo esc_url( $nojs_url ); ?>"><?php esc_html_e( 'View Jetpack Stats without JavaScript', 'jetpack' ); ?></a>.</p>
521 + <div class="wrap">
522 + <div class="stats-odyssey-notice stats-odyssey-notice--content__highlighted">
523 + <div class="stats-odyssey-notice--content">
524 + <h2 class="stats-odyssey-notice--content-header"><?php esc_html_e( 'Deprecated Jetpack Stats Experience', 'jetpack' ); ?></h2>
525 + <p class="stats-odyssey-notice--content-text"><?php esc_html_e( 'The old Jetpack Stats has been deprecated. Please click the button to enable the new experience.', 'jetpack' ); ?></p>
526 + <div class="stats-odyssey-notice--action-bar">
527 + <button class="dops-button stats-odyssey-notice--primary-button">
528 + <a class="is-primary-link" href="<?php echo esc_url( $redirect_url ); ?>"><?php esc_html_e( 'Switch to new Stats', 'jetpack' ); ?></a>
529 + </button>
530 + <a class="is-secondary-link" href="<?php echo esc_url( $learn_url ); ?>" rel="noopener noreferrer" target="_blank"><?php esc_html_e( 'Learn about Stats', 'jetpack' ); ?> <svg xmlns="http://www.w3.org/2000/svg" style="vertical-align: middle;" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg></a>
531 + </div>
532 + </div>
533 + <div class="stats-odyssey-notice--image-container"></div>
534 + </div>
502 535 </div>
536 + <p></p>
503 537 </div>
504 538 <?php
505 539 return;
506 540 }
@@ -557,21 +591,20 @@
557 591 }
558 592 } elseif ( null === $vals ) {
559 593 $q[ $var ] = '';
560 594 } elseif ( 'data' === $vals ) {
561 - if ( 'index.php' === substr( $val, 0, 9 ) ) {
595 + if ( str_starts_with( $val, 'index.php' ) ) {
562 596 $q[ $var ] = $val;
563 597 }
564 598 }
565 599 }
566 600
601 + $url = 'https://' . STATS_DASHBOARD_SERVER . '/wp-admin/index.php';
567 602 if ( isset( $_GET['chart'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
568 603 if ( preg_match( '/^[a-z0-9-]+$/', $_GET['chart'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
569 604 $chart = sanitize_title( $_GET['chart'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
570 605 $url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-includes/charts/{$chart}.php";
571 606 }
572 - } else {
573 - $url = 'https://' . STATS_DASHBOARD_SERVER . '/wp-admin/index.php';
574 607 }
575 608
576 609 $url = add_query_arg( $q, $url );
577 610 $method = 'GET';
@@ -579,34 +612,19 @@
579 612 $user_id = 0; // Means use the blog token.
580 613
581 614 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
582 615 $get_code = wp_remote_retrieve_response_code( $get );
583 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
616 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
584 617 stats_print_wp_remote_error( $get, $url );
585 - } else {
586 - if ( ! empty( $get['headers']['content-type'] ) ) {
587 - $type = $get['headers']['content-type'];
588 - if ( substr( $type, 0, 5 ) === 'image' ) {
589 - $img = $get['body'];
590 - header( 'Content-Type: ' . $type );
591 - header( 'Content-Length: ' . strlen( $img ) );
592 - echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
593 - die();
594 - }
618 + } elseif ( ! empty( $get['headers']['content-type'] ) ) {
619 + $type = $get['headers']['content-type'];
620 + if ( str_starts_with( $type, 'image' ) ) {
621 + $img = $get['body'];
622 + header( 'Content-Type: ' . $type );
623 + header( 'Content-Length: ' . strlen( $img ) );
624 + echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
625 + die( 0 );
595 626 }
596 - $body = stats_convert_post_titles( $get['body'] );
597 - $body = stats_convert_chart_urls( $body );
598 - $body = stats_convert_image_urls( $body );
599 - $body = stats_convert_admin_urls( $body );
600 -
601 - // The response can contain either the content to display OR
602 - // the scripts for the chart UI. The following calls inspect the
603 - // response, insert the Odyssey nudge as needed, and make sure
604 - // everything is output correctly.
605 - stats_print_header_section( $body );
606 - stats_print_odyssey_nudge( $body );
607 - stats_print_content_section( $body );
608 - stats_print_chart_scripts( $body );
609 627 }
610 628
611 629 if ( isset( $_GET['page'] ) && 'stats' === $_GET['page'] && ! isset( $_GET['chart'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
612 630 $tracking = new Tracking();
@@ -613,246 +631,13 @@
613 631 $tracking->record_user_event( 'wpa_page_view', array( 'path' => 'old_stats' ) );
614 632 }
615 633
616 634 if ( isset( $_GET['noheader'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
617 - die;
635 + die( 0 );
618 636 }
619 637 }
620 638
621 639 /**
622 - * Legacy Stats: Print Header Section
623 - *
624 - * @access public
625 - * @param mixed $html HTML.
626 - * @return void
627 - */
628 -function stats_print_header_section( $html ) {
629 - $header = stats_parse_header_section( $html );
630 - if ( $header !== '' ) {
631 - echo $header; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
632 - }
633 -}
634 -
635 -/**
636 - * Legacy Stats: Print Content Section
637 - *
638 - * @access public
639 - * @param mixed $html HTML.
640 - * @return void
641 - */
642 -function stats_print_content_section( $html ) {
643 - $content = stats_parse_content_section( $html );
644 - if ( $content !== '' ) {
645 - echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
646 - }
647 -}
648 -
649 -/**
650 - * Legacy Stats: Print Chart Scripts
651 - *
652 - * @access public
653 - * @param mixed $html HTML.
654 - * @return void
655 - */
656 -function stats_print_chart_scripts( $html ) {
657 - if ( is_chart_scripts( $html ) ) {
658 - echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
659 - }
660 -}
661 -
662 -/**
663 - * Legacy Stats: Test for presence of chart scripts
664 - *
665 - * @access public
666 - * @param mixed $html Input HTML that may or may not include the chart scripts.
667 - * @return bool
668 - */
669 -function is_chart_scripts( $html ) {
670 - $pos = strpos( $html, STATS_CONTENT_MARKER );
671 - return $pos === false;
672 -}
673 -
674 -/**
675 - * Legacy Stats: Parse Header Section
676 - *
677 - * Returns the section of the content up to and including the date header.
678 - *
679 - * @access public
680 - * @param mixed $html HTML.
681 - * @return string
682 - */
683 -function stats_parse_header_section( $html ) {
684 - $head = strstr( $html, STATS_CONTENT_MARKER, true );
685 - // Enforce a string result instead of string|false.
686 - if ( $head === false ) {
687 - return '';
688 - }
689 - return $head;
690 -}
691 -
692 -/**
693 - * Legacy Stats: Print Content Section
694 - *
695 - * Returns the section of the content excluding the date header.
696 - *
697 - * @access public
698 - * @param mixed $html HTML.
699 - * @return string
700 - */
701 -function stats_parse_content_section( $html ) {
702 - $body = strstr( $html, STATS_BODY_MARKER );
703 - // Enforce a string result instead of string|false.
704 - if ( $body === false ) {
705 - return '';
706 - }
707 - return $body;
708 -}
709 -
710 -/**
711 - * Legacy Stats: Determine if we need to show the Odyssey upgrade nudge.
712 - *
713 - * @access public
714 - * @return boolean
715 - */
716 -function stats_should_show_odyssey_nudge() {
717 - $stats_notices = ( new Stats_Notices() )->get_notices_to_show();
718 - return isset( $stats_notices[ Stats_Notices::OPT_IN_NEW_STATS_NOTICE_ID ] )
719 - && $stats_notices[ Stats_Notices::OPT_IN_NEW_STATS_NOTICE_ID ];
720 -}
721 -
722 -/**
723 - * Legacy Stats: Print the Odyssey upgrade nudge.
724 - *
725 - * @access public
726 - * @param mixed $html HTML.
727 - * @return void
728 - */
729 -function stats_print_odyssey_nudge( $html ) {
730 - if ( ! stats_should_show_odyssey_nudge() ) {
731 - return;
732 - }
733 - $pos = strpos( $html, STATS_CONTENT_MARKER );
734 - if ( $pos === false ) {
735 - return;
736 - }
737 - $learn_url = Redirect::get_url( 'jetpack-stats-learn-more' );
738 - $redirect_url = admin_url( 'admin.php?page=stats&enable_new_stats=1' );
739 - ?>
740 - <style>
741 - .stats-odyssey-notice {
742 - display: flex;
743 - font-size: var( --font-body );
744 -
745 - border: 1px solid var( --jp-gray-5 );
746 - border-left-color: var( --jp-black );
747 - border-left-width: 6px;
748 - border-radius: 4px;
749 -
750 - margin-top: 24px;
751 - background: white;
752 - position: relative;
753 - }
754 - .stats-odyssey-notice--content {
755 - padding: 24px 0 24px 30px;
756 - font-size: 2em;
757 - width: 100%;
758 - }
759 - .stats-odyssey-notice--content-header {
760 - font-size: 24px;
761 - line-height: 32px;
762 - margin: 0;
763 - margin-bottom: 8px;
764 - }
765 - .stats-odyssey-notice--content-text {
766 - font-size: 16px;
767 - margin: 0;
768 - }
769 - .stats-odyssey-notice--image-container {
770 - background-image: url("/wp-content/plugins/jetpack/images/odyssey-upgrade/background.png"), url("/wp-content/plugins/jetpack/images/odyssey-upgrade/gradient.png");
771 - background-size: cover;
772 - padding-right: 28px;
773 - width: 100%;
774 - }
775 - .stats-odyssey-notice--close-button {
776 - position: absolute;
777 - top: 1rem;
778 - right: 1rem;
779 - background-color: transparent;
780 - border: none;
781 - cursor: pointer;
782 - }
783 - .stats-odyssey-notice--action-bar {
784 - display: flex;
785 - align-items: center;
786 - margin-top: 24px;
787 - }
788 - .stats-odyssey-notice--primary-button {
789 - margin-right: 18px;
790 - padding-left: 20px;
791 - padding-right: 20px;
792 - font-size: 16px;
793 - border-color: black;
794 - background-color: black;
795 - }
796 - .stats-odyssey-notice--primary-button:hover {
797 - border-color: #3c434a;
798 - background-color: #3c434a;
799 - }
800 - .is-primary-link {
801 - color: white;
802 - text-decoration: none;
803 - }
804 - .is-primary-link:active {
805 - color: white;
806 - }
807 - .is-primary-link:focus {
808 - color: white;
809 - box-shadow: none;
810 - outline: none;
811 - }
812 - .is-primary-link:hover {
813 - color: white;
814 - }
815 - .is-secondary-link {
816 - color: black;
817 - font-size: var( --font-body );
818 - }
819 - .is-secondary-link:hover {
820 - color: black;
821 - }
822 - .is-hidden {
823 - display: none;
824 - }
825 - </style>
826 - <div id="stats-odyssey-nudge-main" class="stats-odyssey-notice">
827 - <div class="stats-odyssey-notice--content">
828 - <h2 class="stats-odyssey-notice--content-header"><?php esc_html_e( 'Explore the new Jetpack Stats', 'jetpack' ); ?></h2>
829 - <p class="stats-odyssey-notice--content-text"><?php esc_html_e( "We've added new stats and insights in a more modern and mobile friendly experience to help you grow your site.", 'jetpack' ); ?></p>
830 - <div class="stats-odyssey-notice--action-bar">
831 - <button class="dops-button stats-odyssey-notice--primary-button">
832 - <a class="is-primary-link" href="<?php echo esc_url( $redirect_url ); ?>"><?php esc_html_e( 'Switch to new Stats', 'jetpack' ); ?></a>
833 - </button>
834 - <a class="is-secondary-link" href="<?php echo esc_url( $learn_url ); ?>" rel="noopener noreferrer" target="_blank"><?php esc_html_e( 'Learn about Stats', 'jetpack' ); ?> <svg xmlns="http://www.w3.org/2000/svg" style="vertical-align: middle;" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg></a>
835 - </div>
836 - </div>
837 - <div class="stats-odyssey-notice--image-container"></div>
838 - <button class="stats-odyssey-notice--close-button" onclick="stats_odyssey_dismiss_nudge()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg></button>
839 - </div>
840 - <?php
841 -}
842 -
843 -/**
844 - * Stats Convert Admin Urls.
845 - *
846 - * @access public
847 - * @param mixed $html HTML.
848 - * @return string
849 - */
850 -function stats_convert_admin_urls( $html ) {
851 - return str_replace( 'index.php?page=stats', 'admin.php?page=stats', $html );
852 -}
853 -
854 -/**
855 640 * Stats Convert Image URLs.
856 641 *
857 642 * @access public
858 643 * @param mixed $html HTML.
@@ -957,8 +742,13 @@
957 742 * @access public
958 743 * @return void
959 744 */
960 745 function stats_admin_bar_head() {
746 + // Let's not show the stats admin bar to users who are not logged in.
747 + if ( ! is_user_logged_in() ) {
748 + return;
749 + }
750 +
961 751 if ( ! Stats_Options::get_option( 'admin_bar' ) ) {
962 752 return;
963 753 }
964 754
@@ -1039,9 +829,9 @@
1039 829
1040 830 $menu = array(
1041 831 'id' => 'stats',
1042 832 'href' => add_query_arg( 'page', 'stats', admin_url( 'admin.php' ) ), // no menu_page_url() blog-side.
1043 - 'title' => "<div><img src='$img_src' srcset='$img_src 1x, $img_src_2x 2x' width='112' height='24' alt='$alt' title='$title'></div>",
833 + 'title' => "<div><img fetchpriority='low' loading='lazy' decoding='async' src='$img_src' srcset='$img_src 1x, $img_src_2x 2x' width='112' height='24' alt='$alt' title='$title'></div>",
1044 834 );
1045 835
1046 836 $wp_admin_bar->add_menu( $menu );
1047 837 }
@@ -1046,21 +836,33 @@
1046 836 $wp_admin_bar->add_menu( $menu );
1047 837 }
1048 838
1049 839 /**
840 + * Adds a Stats link to the site-name admin bar submenu, alongside Dashboard.
1050 841 *
1051 - * Deprecated. The stats module should not update blog details. This is handled by Sync.
1052 - *
1053 - * Stats Update Blog.
1054 - *
1055 842 * @access public
1056 843 * @return void
1057 - *
1058 - * @deprecated since 10.3.
1059 844 */
1060 -function stats_update_blog() {
1061 - _deprecated_function( __METHOD__, 'jetpack-10.3' );
1062 - XMLRPC_Async_Call::add_call( 'jetpack.updateBlog', 0, stats_get_blog() );
845 +function stats_add_link_to_admin_bar_site_menu() {
846 + global $wp_admin_bar;
847 +
848 + if (
849 + ! is_object( $wp_admin_bar ) ||
850 + ! $wp_admin_bar->get_node( 'dashboard' ) ||
851 + ! current_user_can( 'view_stats' ) ||
852 + ( new Host() )->is_wpcom_platform()
853 + ) {
854 + return;
855 + }
856 +
857 + $wp_admin_bar->add_node(
858 + array(
859 + 'parent' => 'site-name',
860 + 'id' => 'jetpack-stats',
861 + 'title' => __( 'Stats', 'jetpack' ),
862 + 'href' => admin_url( 'admin.php?page=stats' ),
863 + )
864 + );
1063 865 }
1064 866
1065 867 /**
1066 868 * Stats Get Blog.
@@ -1250,9 +1052,9 @@
1250 1052 *
1251 1053 * TODO: This should be moved into class-jetpack-stats-dashboard-widget.php.
1252 1054 *
1253 1055 * @access public
1254 - * @return void
1056 + * @return never
1255 1057 */
1256 1058 function stats_dashboard_widget_content() {
1257 1059 $width = isset( $_GET['width'] ) ? intval( $_GET['width'] ) / 2 : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1258 1060 $height = isset( $_GET['height'] ) ? intval( $_GET['height'] ) - 36 : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -1291,9 +1093,9 @@
1291 1093 $user_id = 0; // Means use the blog token.
1292 1094
1293 1095 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1294 1096 $get_code = wp_remote_retrieve_response_code( $get );
1295 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1097 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1296 1098 stats_print_wp_remote_error( $get, $url );
1297 1099 } else {
1298 1100 $body = stats_convert_post_titles( $get['body'] );
1299 1101 $body = stats_convert_chart_urls( $body );
@@ -1318,9 +1120,9 @@
1318 1120 $post_ids[] = $post['post_id'];
1319 1121 }
1320 1122
1321 1123 // Cache.
1322 - get_posts( array( 'include' => join( ',', array_unique( $post_ids ) ) ) );
1124 + get_posts( array( 'include' => implode( ',', array_unique( $post_ids ) ) ) );
1323 1125
1324 1126 $searches = array();
1325 1127 $search_terms = stats_get_csv( 'searchterms', "days=$options[search]$csv_args[search]" );
1326 1128 foreach ( $search_terms as $search_term ) {
@@ -1340,63 +1142,65 @@
1340 1142 <?php esc_html_e( 'View detailed stats', 'jetpack' ); ?>
1341 1143 </a>
1342 1144 </div>
1343 1145 </div>
1344 - <div id="top-posts" class="stats-section">
1345 - <div class="stats-section-inner">
1346 - <h3 class="heading"><?php esc_html_e( 'Top Posts', 'jetpack' ); ?></h3>
1347 - <?php
1348 - if ( empty( $top_posts ) ) {
1349 - ?>
1350 - <p class="nothing"><?php esc_html_e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1146 + <div class="stats-info-content">
1147 + <div id="top-posts" class="stats-section">
1148 + <div class="stats-section-inner">
1149 + <h3 class="heading"><?php esc_html_e( 'Top Posts', 'jetpack' ); ?></h3>
1351 1150 <?php
1352 - } else {
1353 - foreach ( $top_posts as $post ) {
1354 - if ( ! get_post( $post['post_id'] ) ) {
1355 - continue;
1356 - }
1151 + if ( empty( $top_posts ) ) {
1357 1152 ?>
1358 - <p>
1153 + <p class="nothing"><?php esc_html_e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1359 1154 <?php
1360 - printf(
1361 - esc_html(
1362 - /* Translators: Stats dashboard widget Post list with view count: "Post Title 1 View (or Views if plural)". */
1363 - _n( '%1$s %2$s View', '%1$s %2$s Views', $post['views'], 'jetpack' )
1364 - ),
1365 - '<a href="' . esc_url( get_permalink( $post['post_id'] ) ) . '">' . esc_html( get_the_title( $post['post_id'] ) ) . '</a>',
1366 - esc_html( number_format_i18n( $post['views'] ) )
1367 - );
1155 + } else {
1156 + foreach ( $top_posts as $post ) {
1157 + if ( ! get_post( $post['post_id'] ) ) {
1158 + continue;
1159 + }
1160 + ?>
1161 + <p>
1162 + <?php
1163 + printf(
1164 + esc_html(
1165 + /* Translators: Stats dashboard widget Post list with view count: "Post Title 1 View (or Views if plural)". */
1166 + _n( '%1$s %2$s View', '%1$s %2$s Views', $post['views'], 'jetpack' )
1167 + ),
1168 + '<a href="' . esc_url( get_permalink( $post['post_id'] ) ) . '">' . esc_html( get_the_title( $post['post_id'] ) ) . '</a>',
1169 + esc_html( number_format_i18n( $post['views'] ) )
1170 + );
1171 + ?>
1172 + </p>
1173 + <?php
1174 + }
1175 + }
1176 + ?>
1177 + </div>
1178 + </div>
1179 + <div id="top-search" class="stats-section">
1180 + <div class="stats-section-inner">
1181 + <h3 class="heading"><?php esc_html_e( 'Top Searches', 'jetpack' ); ?></h3>
1182 + <?php
1183 + if ( empty( $searches ) ) {
1368 1184 ?>
1369 - </p>
1185 + <p class="nothing"><?php esc_html_e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1370 1186 <?php
1187 + } else {
1188 + foreach ( $searches as $search_term_item ) {
1189 + printf(
1190 + '<p>%s</p>',
1191 + esc_html( $search_term_item )
1192 + );
1193 + }
1371 1194 }
1372 - }
1373 - ?>
1374 - </div>
1375 - </div>
1376 - <div id="top-search" class="stats-section">
1377 - <div class="stats-section-inner">
1378 - <h3 class="heading"><?php esc_html_e( 'Top Searches', 'jetpack' ); ?></h3>
1379 - <?php
1380 - if ( empty( $searches ) ) {
1381 1195 ?>
1382 - <p class="nothing"><?php esc_html_e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1383 - <?php
1384 - } else {
1385 - foreach ( $searches as $search_term_item ) {
1386 - printf(
1387 - '<p>%s</p>',
1388 - esc_html( $search_term_item )
1389 - );
1390 - }
1391 - }
1392 - ?>
1196 + </div>
1393 1197 </div>
1394 1198 </div>
1395 1199 </div>
1396 1200 </div>
1397 1201 <?php
1398 - exit;
1202 + exit( 0 );
1399 1203 }
1400 1204
1401 1205 /**
1402 1206 * Stats Print WP Remote Error.
@@ -1408,9 +1212,9 @@
1408 1212 */
1409 1213 function stats_print_wp_remote_error( $get, $url ) {
1410 1214 $state_name = 'stats_remote_error_' . substr( md5( $url ), 0, 8 );
1411 1215 $previous_error = Jetpack::state( $state_name );
1412 - $error = md5( wp_json_encode( compact( 'get', 'url' ) ) );
1216 + $error = md5( wp_json_encode( compact( 'get', 'url' ), JSON_UNESCAPED_SLASHES ) );
1413 1217 Jetpack::state( $state_name, $error );
1414 1218 if ( $error !== $previous_error ) {
1415 1219 ?>
1416 1220 <div class="wrap">
@@ -1470,8 +1274,9 @@
1470 1274 * @type string $days The length of the desired time frame. Default is 30. Maximum 90 days.
1471 1275 * @type int $limit The maximum number of records to return. Default is 10. Maximum 100.
1472 1276 * @type int $post_id The ID of the post to retrieve stats data for
1473 1277 * @type string $summarize If present, summarizes all matching records. Default Null.
1278 + * @type int $blog_id The WordPress.com blog ID to retrieve stats data for. Default is the current blog.
1474 1279 *
1475 1280 * }
1476 1281 *
1477 1282 * @return array {
@@ -1493,13 +1298,13 @@
1493 1298 'days' => false,
1494 1299 'limit' => 3,
1495 1300 'post_id' => false,
1496 1301 'summarize' => '',
1302 + 'blog_id' => Jetpack_Options::get_option( 'id' ),
1497 1303 );
1498 1304
1499 - $args = wp_parse_args( $args, $defaults );
1500 - $args['table'] = $table;
1501 - $args['blog_id'] = Jetpack_Options::get_option( 'id' );
1305 + $args = wp_parse_args( $args, $defaults );
1306 + $args['table'] = $table;
1502 1307
1503 1308 $stats_csv_url = add_query_arg( $args, 'https://stats.wordpress.com/csv.php' );
1504 1309
1505 1310 $key = md5( $stats_csv_url );
@@ -1569,9 +1374,9 @@
1569 1374 $user_id = 0; // Blog token.
1570 1375
1571 1376 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1572 1377 $get_code = wp_remote_retrieve_response_code( $get );
1573 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1378 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1574 1379 return array(); // @todo: return an error?
1575 1380 } else {
1576 1381 return stats_str_getcsv( $get['body'] );
1577 1382 }
@@ -1582,13 +1387,19 @@
1582 1387 *
1583 1388 * @since 9.7.0 Remove custom handling since str_getcsv is available on all servers running this now.
1584 1389 *
1585 1390 * @param mixed $csv CSV.
1586 - * @return array.
1391 + * @return array
1587 1392 */
1588 1393 function stats_str_getcsv( $csv ) {
1589 - $lines = str_getcsv( $csv, "\n" );
1590 - return array_map( 'str_getcsv', $lines );
1394 + // @todo Correctly handle embedded newlines. Note, despite claims online, `str_getcsv( $csv, "\n" )` does not actually work.
1395 + $lines = explode( "\n", rtrim( $csv, "\n" ) );
1396 + return array_map(
1397 + function ( $line ) {
1398 + return str_getcsv( $line, ',', '"', '' );
1399 + },
1400 + $lines
1401 + );
1591 1402 }
1592 1403
1593 1404 /**
1594 1405 * Abstract out building the rest api stats path.
@@ -1608,9 +1419,9 @@
1608 1419 * @access public
1609 1420 * @deprecated 11.5 Use WPCOM_Stats available methodsinstead.
1610 1421 * @param array $args (default: array()) The args that are passed to the endpoint.
1611 1422 * @param string $resource (default: '') Optional sub-endpoint following /stats/.
1612 - * @return array|WP_Error.
1423 + * @return array|WP_Error
1613 1424 */
1614 1425 function stats_get_from_restapi( $args = array(), $resource = '' ) {
1615 1426 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Please checkout the methods available in Automattic\Jetpack\Stats\WPCOM_Stats' );
1616 1427 $endpoint = jetpack_stats_api_path( $resource );
@@ -1615,9 +1426,9 @@
1615 1426 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Please checkout the methods available in Automattic\Jetpack\Stats\WPCOM_Stats' );
1616 1427 $endpoint = jetpack_stats_api_path( $resource );
1617 1428 $api_version = '1.1';
1618 1429 $args = wp_parse_args( $args, array() );
1619 - $cache_key = md5( implode( '|', array( $endpoint, $api_version, wp_json_encode( $args ) ) ) );
1430 + $cache_key = md5( implode( '|', array( $endpoint, $api_version, wp_json_encode( $args, JSON_UNESCAPED_SLASHES ) ) ) );
1620 1431
1621 1432 $transient_name = "jetpack_restapi_stats_cache_{$cache_key}";
1622 1433
1623 1434 $stats_cache = get_transient( $transient_name );
@@ -1654,93 +1465,8 @@
1654 1465 return $return;
1655 1466 }
1656 1467
1657 1468 /**
1658 - * Load CSS needed for Stats column width in WP-Admin area.
1659 - *
1660 - * @since 4.7.0
1661 - */
1662 -function jetpack_stats_load_admin_css() {
1663 - ?>
1664 - <style type="text/css">
1665 - .fixed .column-stats {
1666 - width: 5em;
1667 - }
1668 - </style>
1669 - <?php
1670 -}
1671 -
1672 -/**
1673 - * Set header for column that allows to go to WordPress.com to see an entry's stats.
1674 - *
1675 - * @param array $columns An array of column names.
1676 - *
1677 - * @since 4.7.0
1678 - *
1679 - * @return mixed
1680 - */
1681 -function jetpack_stats_post_table( $columns ) {
1682 - // Adds a stats link on the edit posts page.
1683 - if ( ! current_user_can( 'view_stats' ) || ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected() ) {
1684 - return $columns;
1685 - }
1686 -
1687 - // Array-Fu to add before comments.
1688 - $pos = array_search( 'comments', array_keys( $columns ), true );
1689 -
1690 - // Fallback to the last position if the post type does not support comments.
1691 - if ( ! is_int( $pos ) ) {
1692 - $pos = count( $columns );
1693 - }
1694 -
1695 - // Final fallback, if the array was malformed by another plugin for example.
1696 - if ( ! is_int( $pos ) ) {
1697 - return $columns;
1698 - }
1699 -
1700 - $chunks = array_chunk( $columns, $pos, true );
1701 - $chunks[0]['stats'] = esc_html__( 'Stats', 'jetpack' );
1702 -
1703 - return call_user_func_array( 'array_merge', $chunks );
1704 -}
1705 -
1706 -/**
1707 - * Set content for cell with link to an entry's stats in Odyssey Stats.
1708 - *
1709 - * @param string $column The name of the column to display.
1710 - * @param int $post_id The current post ID.
1711 - *
1712 - * @since 4.7.0
1713 - *
1714 - * @return mixed
1715 - */
1716 -function jetpack_stats_post_table_cell( $column, $post_id ) {
1717 - if ( 'stats' === $column ) {
1718 - if ( 'publish' !== get_post_status( $post_id ) ) {
1719 - printf(
1720 - '<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>',
1721 - esc_html__( 'No stats', 'jetpack' )
1722 - );
1723 - } else {
1724 - $stats_post_url = ! ( new Host() )->is_woa_site() && Stats_Options::get_option( 'enable_odyssey_stats' )
1725 - ? admin_url( sprintf( 'admin.php?page=stats#!/stats/post/%d/%d', $post_id, Jetpack_Options::get_option( 'id', 0 ) ) )
1726 - : Redirect::get_url(
1727 - 'calypso-stats-post',
1728 - array(
1729 - 'path' => $post_id,
1730 - )
1731 - );
1732 -
1733 - printf(
1734 - '<a href="%s" title="%s" class="dashicons dashicons-chart-bar" target="_blank"></a>',
1735 - esc_url( $stats_post_url ),
1736 - esc_html__( 'View stats for this post', 'jetpack' )
1737 - );
1738 - }
1739 - }
1740 -}
1741 -
1742 -/**
1743 1469 * Add the Jetpack plugin version to the stats tracking data.
1744 1470 *
1745 1471 * @param array $kvs The stats array in key values.
1746 1472 * @return array
@@ -1757,14 +1483,8 @@
1757 1483 * @param array $stats_array The stats array.
1758 1484 * @return WP_Error|Object|null
1759 1485 */
1760 1486 function convert_stats_array_to_object( $stats_array ) {
1487 + _deprecated_function( __FUNCTION__, 'jetpack-13.2', 'Automattic\Jetpack\Stats\WPCOM_Stats->convert_stats_array_to_object' );
1761 1488
1762 - if ( is_wp_error( $stats_array ) ) {
1763 - return $stats_array;
1764 - }
1765 - $encoded_array = wp_json_encode( $stats_array );
1766 - if ( ! $encoded_array ) {
1767 - return new WP_Error( 'stats_encoding_error', 'Failed to encode stats array' );
1768 - }
1769 - return json_decode( $encoded_array );
1489 + return ( new WPCOM_Stats() )->convert_stats_array_to_object( $stats_array );
1770 1490 }