PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
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 14.1.1 14.2.2 All 502 releases
← All changes | modules/stats.php +47 -140 14.2.216.2 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
@@ -15,10 +15,8 @@
15 15 */
16 16
17 17 use Automattic\Jetpack\Admin_UI\Admin_Menu;
18 18 use Automattic\Jetpack\Connection\Client;
19 -use Automattic\Jetpack\Connection\Manager as Connection_Manager;
20 -use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
21 19 use Automattic\Jetpack\Redirect;
22 20 use Automattic\Jetpack\Stats\Main as Stats;
23 21 use Automattic\Jetpack\Stats\Options as Stats_Options;
24 22 use Automattic\Jetpack\Stats\Tracking_Pixel as Stats_Tracking_Pixel;
@@ -23,13 +21,18 @@
23 21 use Automattic\Jetpack\Stats\Options as Stats_Options;
24 22 use Automattic\Jetpack\Stats\Tracking_Pixel as Stats_Tracking_Pixel;
25 23 use Automattic\Jetpack\Stats\WPCOM_Stats;
26 24 use Automattic\Jetpack\Stats\XMLRPC_Provider as Stats_XMLRPC;
25 +use Automattic\Jetpack\Stats_Admin\Admin_Post_List_Column;
27 26 use Automattic\Jetpack\Stats_Admin\Dashboard as Stats_Dashboard;
28 27 use Automattic\Jetpack\Stats_Admin\Main as Stats_Main;
29 28 use Automattic\Jetpack\Status\Host;
30 29 use Automattic\Jetpack\Tracking;
31 30
31 +if ( ! defined( 'ABSPATH' ) ) {
32 + exit( 0 );
33 +}
34 +
32 35 if ( defined( 'STATS_DASHBOARD_SERVER' ) ) {
33 36 return;
34 37 }
35 38
@@ -58,18 +61,15 @@
58 61 add_action( 'admin_head', 'stats_admin_bar_head', 100 );
59 62 add_action( 'wp_head', 'stats_admin_bar_head', 100 );
60 63 }
61 64
65 + Admin_Post_List_Column::register();
66 +
62 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' );
63 69
64 70 add_filter( 'pre_option_db_version', 'stats_ignore_db_version' );
65 71
66 - // Add an icon to see stats in WordPress.com for a particular post.
67 - add_action( 'admin_print_styles-edit.php', 'jetpack_stats_load_admin_css' );
68 - add_filter( 'manage_posts_columns', 'jetpack_stats_post_table' );
69 - add_filter( 'manage_pages_columns', 'jetpack_stats_post_table' );
70 - add_action( 'manage_posts_custom_column', 'jetpack_stats_post_table_cell', 10, 2 );
71 - add_action( 'manage_pages_custom_column', 'jetpack_stats_post_table_cell', 10, 2 );
72 72 // Filter for adding the Jetpack plugin version to tracking stats.
73 73 add_filter( 'stats_array', 'filter_stats_array_add_jp_version' );
74 74
75 75 require_once __DIR__ . '/stats/class-jetpack-stats-upgrade-nudges.php';
@@ -232,9 +232,9 @@
232 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 );
233 233 $relative_pos = strpos( $redirect_url, '/wp-admin/' );
234 234 if ( false !== $relative_pos ) {
235 235 wp_safe_redirect( admin_url( substr( $redirect_url, $relative_pos + 10 ) ) );
236 - exit;
236 + exit( 0 );
237 237 }
238 238 }
239 239
240 240 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -252,11 +252,9 @@
252 252 $hook = Admin_Menu::add_menu( __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'stats', 'jetpack_admin_ui_stats_report_page_wrapper' );
253 253 add_action( "load-$hook", 'stats_reports_load' );
254 254 } else {
255 255 // Enable the new Odyssey Stats experience.
256 - $stats_dashboard = new Stats_Dashboard();
257 - $hook = Admin_Menu::add_menu( __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'stats', array( $stats_dashboard, 'render' ), 1 );
258 - add_action( "load-$hook", array( $stats_dashboard, 'admin_init' ) );
256 + Stats_Dashboard::init();
259 257 }
260 258 }
261 259
262 260 /**
@@ -316,10 +314,10 @@
316 314 element.classList.toggle( "is-hidden" );
317 315 // Send an AJAX request.
318 316 // Note we can provide a 'postponed_for' parameter to set the delay.
319 317 // Without a parameter it defaults to 30 days which is what we want here.
320 - let nonce = <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ) ); ?>;
321 - let url = <?php echo wp_json_encode( rest_url( '/jetpack/v4/stats-app/stats/notices' ) ); ?>;
318 + let nonce = <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
319 + let url = <?php echo wp_json_encode( rest_url( '/jetpack/v4/stats-app/stats/notices' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
322 320 let data = {
323 321 id: 'opt_in_new_stats',
324 322 status: 'postponed',
325 323 };
@@ -362,9 +360,9 @@
362 360 $parsed = wp_parse_url( admin_url() );
363 361 ?>
364 362 <script type="text/javascript">
365 363 /* <![CDATA[ */
366 -document.cookie = 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=<?php echo esc_js( $parsed['path'] ); ?>';
364 +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 ); ?>;
367 365 /* ]]> */
368 366 </script>
369 367 <?php
370 368 }
@@ -388,9 +386,9 @@
388 386 */
389 387 function stats_reports_page( $main_chart_only = false ) {
390 388 if ( isset( $_GET['dashboard'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
391 389 stats_dashboard_widget_content();
392 - exit; // @phan-suppress-current-line PhanPluginUnreachableCode -- Safer to include it even though stats_dashboard_widget_content() never returns.
390 + exit( 0 ); // @phan-suppress-current-line PhanPluginUnreachableCode -- Safer to include it even though stats_dashboard_widget_content() never returns.
393 391 }
394 392
395 393 $blog_id = Stats_Options::get_option( 'blog_id' );
396 394 $learn_url = Redirect::get_url( 'jetpack-stats-learn-more' );
@@ -498,9 +496,9 @@
498 496 $i18n_headers = jetpack_get_module_i18n( 'stats' );
499 497 ?>
500 498 <a
501 499 style="font-size:13px;"
502 - href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack#/settings?term=' . rawurlencode( $i18n_headers['name'] ) ) ); ?>"
500 + href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack#/settings?term=' . rawurlencode( $i18n_headers['name'] ?? '' ) ) ); ?>"
503 501 >
504 502 <?php esc_html_e( 'Configure', 'jetpack' ); ?>
505 503 </a>
506 504 <?php
@@ -603,9 +601,9 @@
603 601 $user_id = 0; // Means use the blog token.
604 602
605 603 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
606 604 $get_code = wp_remote_retrieve_response_code( $get );
607 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
605 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
608 606 stats_print_wp_remote_error( $get, $url );
609 607 } elseif ( ! empty( $get['headers']['content-type'] ) ) {
610 608 $type = $get['headers']['content-type'];
611 609 if ( str_starts_with( $type, 'image' ) ) {
@@ -612,9 +610,9 @@
612 610 $img = $get['body'];
613 611 header( 'Content-Type: ' . $type );
614 612 header( 'Content-Length: ' . strlen( $img ) );
615 613 echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
616 - die();
614 + die( 0 );
617 615 }
618 616 }
619 617
620 618 if ( isset( $_GET['page'] ) && 'stats' === $_GET['page'] && ! isset( $_GET['chart'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -622,9 +620,9 @@
622 620 $tracking->record_user_event( 'wpa_page_view', array( 'path' => 'old_stats' ) );
623 621 }
624 622
625 623 if ( isset( $_GET['noheader'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
626 - die;
624 + die( 0 );
627 625 }
628 626 }
629 627
630 628 /**
@@ -820,9 +818,9 @@
820 818
821 819 $menu = array(
822 820 'id' => 'stats',
823 821 'href' => add_query_arg( 'page', 'stats', admin_url( 'admin.php' ) ), // no menu_page_url() blog-side.
824 - 'title' => "<div><img src='$img_src' srcset='$img_src 1x, $img_src_2x 2x' width='112' height='24' alt='$alt' title='$title'></div>",
822 + '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>",
825 823 );
826 824
827 825 $wp_admin_bar->add_menu( $menu );
828 826 }
@@ -827,21 +825,33 @@
827 825 $wp_admin_bar->add_menu( $menu );
828 826 }
829 827
830 828 /**
829 + * Adds a Stats link to the site-name admin bar submenu, alongside Dashboard.
831 830 *
832 - * Deprecated. The stats module should not update blog details. This is handled by Sync.
833 - *
834 - * Stats Update Blog.
835 - *
836 831 * @access public
837 832 * @return void
838 - *
839 - * @deprecated since 10.3.
840 833 */
841 -function stats_update_blog() {
842 - _deprecated_function( __METHOD__, 'jetpack-10.3' );
843 - XMLRPC_Async_Call::add_call( 'jetpack.updateBlog', 0, stats_get_blog() );
834 +function stats_add_link_to_admin_bar_site_menu() {
835 + global $wp_admin_bar;
836 +
837 + if (
838 + ! is_object( $wp_admin_bar ) ||
839 + ! $wp_admin_bar->get_node( 'dashboard' ) ||
840 + ! current_user_can( 'view_stats' ) ||
841 + ( new Host() )->is_wpcom_platform()
842 + ) {
843 + return;
844 + }
845 +
846 + $wp_admin_bar->add_node(
847 + array(
848 + 'parent' => 'site-name',
849 + 'id' => 'jetpack-stats',
850 + 'title' => __( 'Stats', 'jetpack' ),
851 + 'href' => admin_url( 'admin.php?page=stats' ),
852 + )
853 + );
844 854 }
845 855
846 856 /**
847 857 * Stats Get Blog.
@@ -1072,9 +1082,9 @@
1072 1082 $user_id = 0; // Means use the blog token.
1073 1083
1074 1084 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1075 1085 $get_code = wp_remote_retrieve_response_code( $get );
1076 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1086 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1077 1087 stats_print_wp_remote_error( $get, $url );
1078 1088 } else {
1079 1089 $body = stats_convert_post_titles( $get['body'] );
1080 1090 $body = stats_convert_chart_urls( $body );
@@ -1177,9 +1187,9 @@
1177 1187 </div>
1178 1188 </div>
1179 1189 </div>
1180 1190 <?php
1181 - exit;
1191 + exit( 0 );
1182 1192 }
1183 1193
1184 1194 /**
1185 1195 * Stats Print WP Remote Error.
@@ -1191,9 +1201,9 @@
1191 1201 */
1192 1202 function stats_print_wp_remote_error( $get, $url ) {
1193 1203 $state_name = 'stats_remote_error_' . substr( md5( $url ), 0, 8 );
1194 1204 $previous_error = Jetpack::state( $state_name );
1195 - $error = md5( wp_json_encode( compact( 'get', 'url' ) ) );
1205 + $error = md5( wp_json_encode( compact( 'get', 'url' ), JSON_UNESCAPED_SLASHES ) );
1196 1206 Jetpack::state( $state_name, $error );
1197 1207 if ( $error !== $previous_error ) {
1198 1208 ?>
1199 1209 <div class="wrap">
@@ -1353,9 +1363,9 @@
1353 1363 $user_id = 0; // Blog token.
1354 1364
1355 1365 $get = Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1356 1366 $get_code = wp_remote_retrieve_response_code( $get );
1357 - if ( is_wp_error( $get ) || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1367 + if ( is_wp_error( $get ) || $get_code === '' || ( 2 !== (int) ( $get_code / 100 ) && 304 !== $get_code ) || empty( $get['body'] ) ) {
1358 1368 return array(); // @todo: return an error?
1359 1369 } else {
1360 1370 return stats_str_getcsv( $get['body'] );
1361 1371 }
@@ -1373,10 +1383,9 @@
1373 1383 // @todo Correctly handle embedded newlines. Note, despite claims online, `str_getcsv( $csv, "\n" )` does not actually work.
1374 1384 $lines = explode( "\n", rtrim( $csv, "\n" ) );
1375 1385 return array_map(
1376 1386 function ( $line ) {
1377 - // @todo When we drop support for PHP <7.4, consider passing empty-string for `$escape` here for better spec compatibility.
1378 - return str_getcsv( $line, ',', '"', '\\' );
1387 + return str_getcsv( $line, ',', '"', '' );
1379 1388 },
1380 1389 $lines
1381 1390 );
1382 1391 }
@@ -1406,9 +1415,9 @@
1406 1415 _deprecated_function( __METHOD__, 'jetpack-11.5', 'Please checkout the methods available in Automattic\Jetpack\Stats\WPCOM_Stats' );
1407 1416 $endpoint = jetpack_stats_api_path( $resource );
1408 1417 $api_version = '1.1';
1409 1418 $args = wp_parse_args( $args, array() );
1410 - $cache_key = md5( implode( '|', array( $endpoint, $api_version, wp_json_encode( $args ) ) ) );
1419 + $cache_key = md5( implode( '|', array( $endpoint, $api_version, wp_json_encode( $args, JSON_UNESCAPED_SLASHES ) ) ) );
1411 1420
1412 1421 $transient_name = "jetpack_restapi_stats_cache_{$cache_key}";
1413 1422
1414 1423 $stats_cache = get_transient( $transient_name );
@@ -1442,110 +1451,8 @@
1442 1451 // To reduce size in storage: store with time as key, store JSON encoded data (unless error).
1443 1452 set_transient( $transient_name, array( time() => $data ), 5 * MINUTE_IN_SECONDS );
1444 1453
1445 1454 return $return;
1446 -}
1447 -
1448 -/**
1449 - * Load CSS needed for Stats column width in WP-Admin area.
1450 - *
1451 - * @since 4.7.0
1452 - */
1453 -function jetpack_stats_load_admin_css() {
1454 - ?>
1455 - <style type="text/css">
1456 - .fixed .column-stats {
1457 - width: 5em;
1458 - }
1459 - </style>
1460 - <?php
1461 -}
1462 -
1463 -/**
1464 - * Set header for column that allows to view an entry's stats.
1465 - *
1466 - * @param array $columns An array of column names.
1467 - *
1468 - * @since 4.7.0
1469 - *
1470 - * @return mixed
1471 - */
1472 -function jetpack_stats_post_table( $columns ) {
1473 - /*
1474 - * Stats can be accessed in wp-admin or in Calypso,
1475 - * depending on what version of the stats screen is enabled on your site.
1476 - *
1477 - * In both cases, the user must be allowed to access stats.
1478 - *
1479 - * If the Odyssey Stats experience isn't enabled, the user will need to go to Calypso,
1480 - * so they need to be connected to WordPress.com to be able to access that page.
1481 - */
1482 - if (
1483 - ! current_user_can( 'view_stats' )
1484 - || (
1485 - ! Stats_Options::get_option( 'enable_odyssey_stats' )
1486 - && ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected()
1487 - )
1488 - ) {
1489 - return $columns;
1490 - }
1491 -
1492 - // Array-Fu to add before comments.
1493 - $pos = array_search( 'comments', array_keys( $columns ), true );
1494 -
1495 - // Fallback to the last position if the post type does not support comments.
1496 - if ( ! is_int( $pos ) ) {
1497 - $pos = count( $columns );
1498 - }
1499 -
1500 - // Final fallback, if the array was malformed by another plugin for example.
1501 - if ( ! is_int( $pos ) ) {
1502 - return $columns;
1503 - }
1504 -
1505 - $chunks = array_chunk( $columns, $pos, true );
1506 - $chunks[0]['stats'] = esc_html__( 'Stats', 'jetpack' );
1507 -
1508 - return call_user_func_array( 'array_merge', $chunks );
1509 -}
1510 -
1511 -/**
1512 - * Set content for cell with link to an entry's stats in Odyssey Stats.
1513 - *
1514 - * @param string $column The name of the column to display.
1515 - * @param int $post_id The current post ID.
1516 - *
1517 - * @since 4.7.0
1518 - *
1519 - * @return mixed
1520 - */
1521 -function jetpack_stats_post_table_cell( $column, $post_id ) {
1522 - if ( 'stats' === $column ) {
1523 - if ( 'publish' !== get_post_status( $post_id ) ) {
1524 - printf(
1525 - '<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>',
1526 - esc_html__( 'No stats', 'jetpack' )
1527 - );
1528 - } else {
1529 - // Link to the wp-admin stats page.
1530 - $stats_post_url = admin_url( sprintf( 'admin.php?page=stats#!/stats/post/%d/%d', $post_id, Jetpack_Options::get_option( 'id', 0 ) ) );
1531 - // Unless the user is on a Default style WOA site, in which case link to Calypso.
1532 - if ( ( new Host() )->is_woa_site() && Stats_Options::get_option( 'enable_odyssey_stats' ) && 'wp-admin' !== get_option( 'wpcom_admin_interface' ) ) {
1533 - $stats_post_url = Redirect::get_url(
1534 - 'calypso-stats-post',
1535 - array(
1536 - 'path' => $post_id,
1537 - )
1538 - );
1539 - }
1540 -
1541 - printf(
1542 - '<a href="%s" title="%s" class="dashicons dashicons-chart-bar" target="_blank"></a>',
1543 - esc_url( $stats_post_url ),
1544 - esc_html__( 'View stats for this post', 'jetpack' )
1545 - );
1546 - }
1547 - }
1548 1455 }
1549 1456
1550 1457 /**
1551 1458 * Add the Jetpack plugin version to the stats tracking data.