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 | class.jetpack.php +1055 -1197 12.1.3 → 16.3-a.7 View file →
@@ -7,31 +7,38 @@
7 7 * @package automattic/jetpack
8 8 */
9 9
10 10 use Automattic\Jetpack\Assets;
11 -use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
11 +use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
12 12 use Automattic\Jetpack\Config;
13 +use Automattic\Jetpack\Connection\Authorize_Json_Api;
13 14 use Automattic\Jetpack\Connection\Client;
14 -use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
15 15 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
16 -use Automattic\Jetpack\Connection\Nonce_Handler;
17 16 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
18 17 use Automattic\Jetpack\Connection\Secrets;
19 18 use Automattic\Jetpack\Connection\Tokens;
20 -use Automattic\Jetpack\Connection\Utils as Connection_Utils;
19 +use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
21 20 use Automattic\Jetpack\Constants;
22 21 use Automattic\Jetpack\CookieState;
22 +use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
23 23 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
24 24 use Automattic\Jetpack\Errors;
25 +use Automattic\Jetpack\Feature_Policy;
25 26 use Automattic\Jetpack\Files;
27 +use Automattic\Jetpack\Heartbeat;
26 28 use Automattic\Jetpack\Identity_Crisis;
29 +use Automattic\Jetpack\Import\Main as Import_Main;
27 30 use Automattic\Jetpack\Licensing;
28 31 use Automattic\Jetpack\Modules;
29 32 use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
30 -use Automattic\Jetpack\Partner;
33 +use Automattic\Jetpack\Newsletter\Reader_Link;
31 34 use Automattic\Jetpack\Paths;
35 +use Automattic\Jetpack\Plugin\Deprecate;
32 36 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
37 +use Automattic\Jetpack\Podcast\Podcast;
33 38 use Automattic\Jetpack\Redirect;
39 +use Automattic\Jetpack\Scan_Page\Jetpack_Scan as Scan_Page_Init;
40 +use Automattic\Jetpack\SEO\Initializer as Jetpack_SEO_Initializer;
34 41 use Automattic\Jetpack\Status;
35 42 use Automattic\Jetpack\Status\Host;
36 43 use Automattic\Jetpack\Status\Visitor;
37 44 use Automattic\Jetpack\Sync\Actions as Sync_Actions;
@@ -38,9 +45,14 @@
38 45 use Automattic\Jetpack\Sync\Health;
39 46 use Automattic\Jetpack\Sync\Sender;
40 47 use Automattic\Jetpack\Terms_Of_Service;
41 48 use Automattic\Jetpack\Tracking;
49 +use Automattic\Woocommerce_Analytics;
42 50
51 +if ( ! defined( 'ABSPATH' ) ) {
52 + exit( 0 );
53 +}
54 +
43 55 /*
44 56 Options:
45 57 jetpack_options (array)
46 58 An array of options.
@@ -75,48 +87,8 @@
75 87 */
76 88 public $xmlrpc_server = null;
77 89
78 90 /**
79 - * The handles of styles that are concatenated into jetpack.css.
80 - *
81 - * When making changes to that list, you must also update concat_list in tools/webpack.config.css.js.
82 - *
83 - * @var array The handles of styles that are concatenated into jetpack.css.
84 - */
85 - public $concatenated_style_handles = array(
86 - 'jetpack-carousel-swiper-css',
87 - 'jetpack-carousel',
88 - 'grunion.css',
89 - 'the-neverending-homepage',
90 - 'jetpack_likes',
91 - 'jetpack_related-posts',
92 - 'sharedaddy',
93 - 'jetpack-slideshow',
94 - 'presentations',
95 - 'quiz',
96 - 'jetpack-subscriptions',
97 - 'jetpack-responsive-videos-style',
98 - 'jetpack-social-menu',
99 - 'tiled-gallery',
100 - 'jetpack_display_posts_widget',
101 - 'gravatar-profile-widget',
102 - 'goodreads-widget',
103 - 'jetpack_social_media_icons_widget',
104 - 'jetpack-top-posts-widget',
105 - 'jetpack_image_widget',
106 - 'jetpack-my-community-widget',
107 - 'jetpack-authors-widget',
108 - 'wordads',
109 - 'eu-cookie-law-style',
110 - 'flickr-widget-style',
111 - 'jetpack-search-widget',
112 - 'jetpack-simple-payments-widget-style',
113 - 'jetpack-widget-social-icons-styles',
114 - 'wpcom_instagram_widget',
115 - 'milestone-widget',
116 - );
117 -
118 - /**
119 91 * Contains all assets that have had their URL rewritten to minified versions.
120 92 *
121 93 * @var array
122 94 */
@@ -131,11 +103,8 @@
131 103 'contact-form' => array(
132 104 array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
133 105 array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
134 106 ),
135 - 'custom-css' => array(
136 - array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
137 - ),
138 107 'gravatar-hovercards' => array(
139 108 array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
140 109 ),
141 110 'latex' => array(
@@ -296,13 +265,8 @@
296 265 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
297 266 'Rank Math' => 'seo-by-rank-math/rank-math.php',
298 267 'Slim SEO' => 'slim-seo/slim-seo.php',
299 268 ),
300 - 'lazy-images' => array(
301 - 'Lazy Load' => 'lazy-load/lazy-load.php',
302 - 'BJ Lazy Load' => 'bj-lazy-load/bj-lazy-load.php',
303 - 'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
304 - ),
305 269 );
306 270
307 271 /**
308 272 * Plugins for which we turn off our Facebook OG Tags implementation.
@@ -311,9 +275,8 @@
311 275 * Graph tags via filter when their Social Meta modules are active:
312 276 *
313 277 * - All in One SEO Pack, All in one SEO Pack Pro
314 278 * - WordPress SEO by Yoast, WordPress SEO Premium by Yoast
315 - * - SEOPress, SEOPress Pro
316 279 *
317 280 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
318 281 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
319 282 *
@@ -356,8 +319,10 @@
356 319 'wp-caregiver/wp-caregiver.php', // WP Caregiver.
357 320 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta.
358 321 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
359 322 'wp-ogp/wp-ogp.php', // WP-OGP.
323 + 'wp-seopress/seopress.php', // SEOPress.
324 + 'wp-seopress-pro/seopress-pro.php', // SEOPress Pro.
360 325 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
361 326 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button.
362 327 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox.
363 328 'seo-by-rank-math/rank-math.php', // Rank Math.
@@ -424,8 +389,10 @@
424 389
425 390 /**
426 391 * Verified data for JSON authorization request
427 392 *
393 + * @deprecated 13.4
394 + *
428 395 * @var array
429 396 */
430 397 public $json_api_authorization_request = array();
431 398
@@ -466,8 +433,16 @@
466 433 */
467 434 public static $instance = false;
468 435
469 436 /**
437 + * Resolved answer for `is_premium_analytics_enabled()`, or null before the first call.
438 + *
439 + * @since 16.1
440 + * @var bool|null
441 + */
442 + private static $premium_analytics_enabled = null;
443 +
444 + /**
470 445 * Singleton
471 446 *
472 447 * @static
473 448 */
@@ -487,17 +462,21 @@
487 462 public function plugin_upgrade() {
488 463 if ( self::is_connection_ready() ) {
489 464 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
490 465 if ( JETPACK__VERSION !== $version ) {
491 - // Prevent multiple upgrades at once - only a single process should trigger
492 - // an upgrade to avoid stampedes.
493 - if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
494 - return;
466 + // Prevent multiple upgrades at once - only a single process should trigger an upgrade to avoid stampedes.
467 + if ( wp_using_ext_object_cache() ) {
468 + if ( true !== wp_cache_add( self::$plugin_upgrade_lock_key, 1, 'transient', 10 ) ) {
469 + return;
470 + }
471 + } else {
472 + if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
473 + return;
474 + }
475 +
476 + set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
495 477 }
496 478
497 - // Set a short lock to prevent multiple instances of the upgrade.
498 - set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
499 -
500 479 // check which active modules actually exist and remove others from active_modules list.
501 480 $unfiltered_modules = self::get_active_modules();
502 481 $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
503 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
@@ -503,9 +482,9 @@
503 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
504 483 self::update_active_modules( $modules );
505 484 }
506 485
507 - add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
486 + self::register_upgrade_init_hooks();
508 487
509 488 // Upgrade to 4.3.0.
510 489 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
511 490 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
@@ -560,8 +539,16 @@
560 539 Jetpack_Options::delete_option( 'autoupdate_plugins' );
561 540 } // Should we have some type of fallback if something fails here?
562 541 }
563 542
543 + // Set the newsletter send default option for existing sites.
544 + if ( false === get_option( 'wpcom_newsletter_send_default' ) ) {
545 + add_option( 'wpcom_newsletter_send_default', 1 );
546 + }
547 +
548 + // Its handler went with the Recommendations assistant.
549 + wp_clear_scheduled_hook( 'jetpack_recommend_videopress' );
550 +
564 551 if ( did_action( 'wp_loaded' ) ) {
565 552 self::upgrade_on_load();
566 553 } else {
567 554 add_action(
@@ -595,9 +582,8 @@
595 582 }
596 583
597 584 if (
598 585 class_exists( 'Jetpack_Sitemap_Manager' )
599 - && version_compare( JETPACK__VERSION, '5.3', '>=' )
600 586 ) {
601 587 do_action( 'jetpack_sitemaps_purge_data' );
602 588 }
603 589
@@ -612,9 +598,9 @@
612 598 * Also fires Action hooks for each newly activated and deactivated module.
613 599 *
614 600 * @param array $modules Array of active modules to be saved in options.
615 601 *
616 - * @return $success bool true for success, false for failure.
602 + * @return bool $success true for success, false for failure.
617 603 */
618 604 public static function update_active_modules( $modules ) {
619 605 return ( new Modules() )->update_active( $modules );
620 606 }
@@ -644,10 +630,10 @@
644 630 sort( $taken_priorities );
645 631
646 632 $first_priority = array_shift( $taken_priorities );
647 633
648 - if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
649 - $new_priority = - PHP_INT_MAX;
634 + if ( $first_priority <= PHP_INT_MIN ) {
635 + $new_priority = PHP_INT_MIN;
650 636 } else {
651 637 $new_priority = $first_priority - 1;
652 638 }
653 639
@@ -668,32 +654,17 @@
668 654 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
669 655 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
670 656 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
671 657
672 - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
673 -
674 - add_filter(
675 - 'jetpack_signature_check_token',
676 - array( __CLASS__, 'verify_onboarding_token' ),
677 - 10,
678 - 3
679 - );
680 -
681 658 /**
682 659 * Prepare Gutenberg Editor functionality
660 + *
661 + * The hooks previously here have been moved to modules/blocks.php but leaving this here pending
662 + * a longer investigation to see if code is expecting the Gutenberg class to always be available.
683 663 */
684 664 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
685 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
686 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
687 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 );
688 - add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
689 -
690 665 add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
691 666
692 - // Unlink user before deleting the user from WP.com.
693 - add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 );
694 - add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 );
695 -
696 667 add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
697 668
698 669 add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
699 670 add_action( 'login_init', array( $this, 'login_init' ) );
@@ -700,8 +671,13 @@
700 671
701 672 // Set up the REST authentication hooks.
702 673 Connection_Rest_Authentication::init();
703 674
675 + // Register Jetpack-specific connection tests (sync health, etc.) with the connection
676 + // package's health test suite. This runs on all requests (not just admin), because
677 + // the connection/test REST endpoint can be called outside admin context.
678 + add_action( 'jetpack_connection_tests_loaded', array( $this, 'register_jetpack_connection_tests' ) );
679 +
704 680 add_action( 'admin_init', array( $this, 'admin_init' ) );
705 681 add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
706 682
707 683 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
@@ -715,12 +691,8 @@
715 691
716 692 // Returns HTTPS support status.
717 693 add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
718 694
719 - add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
720 -
721 - add_action( 'wp_ajax_jetpack_recommendations_banner', array( 'Jetpack_Recommendations_Banner', 'ajax_callback' ) );
722 -
723 695 add_action( 'wp_loaded', array( $this, 'register_assets' ) );
724 696
725 697 /**
726 698 * These actions run checks to load additional files.
@@ -738,29 +710,8 @@
738 710
739 711 add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
740 712 add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
741 713
742 - require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php';
743 - $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() );
744 - add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) );
745 -
746 - /*
747 - * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
748 - * We should make sure to only do this for front end links.
749 - */
750 - if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
751 - add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
752 - add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
753 -
754 - /*
755 - * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
756 - * so they point moderation links on emails to Calypso.
757 - */
758 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/functions.wp-notify.php';
759 - add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
760 - add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
761 - }
762 -
763 714 add_action(
764 715 'plugins_loaded',
765 716 function () {
766 717 if ( User_Agent_Info::is_mobile_app() ) {
@@ -769,20 +720,12 @@
769 720 }
770 721 );
771 722
772 723 // Update the site's Jetpack plan and products from API on heartbeats.
773 - add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
724 + add_action( 'jetpack_heartbeat', array( Jetpack_Plan::class, 'refresh_from_wpcom' ) );
774 725
775 - /**
776 - * This is the hack to concatenate all css files into one.
777 - * For description and reasoning see the implode_frontend_css method.
778 - *
779 - * Super late priority so we catch all the registered styles.
780 - */
781 - if ( ! is_admin() ) {
782 - add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first.
783 - add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`.
784 - }
726 + // The Connection package fetches the site record for `jetpack/v4/site`; reuse it to refresh the plan.
727 + add_action( 'jetpack_site_data_fetched', array( Jetpack_Plan::class, 'update_from_site_record' ) );
785 728
786 729 // Actually push the stats on shutdown.
787 730 if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
788 731 add_action( 'shutdown', array( $this, 'push_stats' ) );
@@ -790,8 +733,10 @@
790 733
791 734 // After a successful connection.
792 735 add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) );
793 736 add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) );
737 + add_action( 'jetpack_site_registered', array( Reader_Link::class, 'activate_on_connection' ), 9 );
738 + add_action( 'jetpack_site_registered', array( \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::class, 'discard_credentials' ) );
794 739
795 740 // Actions for Manager::authorize().
796 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
797 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
@@ -796,8 +741,9 @@
796 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
797 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
798 743 add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
799 744
745 + Jetpack_Client_Server::init();
800 746 add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) );
801 747 add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) );
802 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
803 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
@@ -802,9 +748,9 @@
802 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
803 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
804 750
805 751 // Filters for the Manager::get_token() urls and request body.
806 - add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
752 + add_filter( 'jetpack_token_redirect_url', array( Authorize_Redirect::class, 'filter_connect_redirect_url' ) );
807 753 add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
808 754
809 755 // Filter for the `jetpack/v4/connection/data` API response.
810 756 add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) );
@@ -825,18 +771,185 @@
825 771 // Make resources use static domain when possible.
826 772 add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) );
827 773
828 774 // Validate the domain names in Jetpack development versions.
829 - add_action( 'jetpack_pre_register', array( get_called_class(), 'registration_check_domains' ) );
775 + add_action( 'jetpack_pre_register', array( static::class, 'registration_check_domains' ) );
830 776
831 777 // Register product descriptions for partner coupon usage.
832 778 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
833 779
834 - // Actions for conditional recommendations.
835 - add_action( 'plugins_loaded', array( 'Jetpack_Recommendations', 'init_conditional_recommendation_actions' ) );
780 + // Add 5-star
781 + add_filter( 'plugin_row_meta', array( $this, 'add_5_star_review_link' ), 10, 2 );
782 + add_action( 'init', array( Deprecate::class, 'instance' ) );
783 +
784 + // Register Jetpack module management abilities (WordPress Abilities API, WP 6.9+).
785 + \Automattic\Jetpack\Plugin\Abilities\Modules_Abilities::init();
786 +
787 + // Register Connection abilities (WordPress Abilities API, WP 6.9+). Scoped to the
788 + // Jetpack plugin for now: the Connection package no longer auto-wires these, so
789 + // connection-only consumers (Boost, Protect, Search, etc.) do not register them yet.
790 + \Automattic\Jetpack\Connection\Abilities\Connection_Abilities::init();
836 791 }
837 792
838 793 /**
794 + * Whether the current request should eagerly initialize the admin/REST-only
795 + * packages (the Import package and My Jetpack) now, at `plugins_loaded` time.
796 + *
797 + * Returns true for admin, cron, POST, and WP-CLI requests — the contexts,
798 + * knowable this early, where those packages have work to do. Returns false
799 + * for a plain front-end GET *and* for a REST request: the two can't be told
800 + * apart yet (this runs before `rest_api_init`), so callers defer the REST
801 + * case by initializing the package on `rest_api_init` instead, while a plain
802 + * page view never fires that hook and so loads nothing. This keeps
803 + * admin/REST-only PHP out of opcache on the front-end GET hot path.
804 + *
805 + * No in-repo code depends on the deferral. The one externally observable
806 + * change is timing: the packages' documented init hooks
807 + * (`jetpack_import_initialized`, `jetpack_feature_import_enabled`, and
808 + * `my_jetpack_init`) no longer fire on a plain front-end GET — they fire on
809 + * the admin, cron, POST, WP-CLI, and REST requests where the packages load.
810 + *
811 + * @return bool
812 + */
813 + private static function should_eager_load_packages() {
814 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) );
815 + $is_wp_cli = Constants::is_true( 'WP_CLI' );
816 +
817 + return is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli;
818 + }
819 +
820 + /**
821 + * Configure the Import package from a deferred hook.
822 + *
823 + * The eager path uses Config::ensure( 'import' ), but the deferred REST path
824 + * runs after Config::on_plugins_loaded() has already processed its feature
825 + * flags, so it needs a hookable bootstrap callback. Preserve Config's
826 + * feature-enabled action for hook consumers.
827 + *
828 + * @since 16.0
829 + *
830 + * @return void
831 + */
832 + public static function configure_import_package() {
833 + if ( class_exists( Import_Main::class ) ) {
834 + Import_Main::configure();
835 +
836 + if ( ! did_action( 'jetpack_feature_import_enabled' ) ) {
837 + do_action( 'jetpack_feature_import_enabled' );
838 + }
839 + }
840 + }
841 +
842 + /**
843 + * Enable the bundled Backup dashboard.
844 + *
845 + * The standalone plugin initializes the package first, so with both active this is a no-op.
846 + *
847 + * @return void
848 + */
849 + public static function configure_backup_package() {
850 + // Not offered on multisite, which the Backup package does not support, or without a
851 + // connected owner, since buying and managing backups needs a linked account.
852 + if ( is_multisite() || ! self::is_connection_ready() || ! self::connection()->has_connected_owner() ) {
853 + return;
854 + }
855 +
856 + /**
857 + * Filters whether the Jetpack plugin offers its bundled Backup dashboard.
858 + *
859 + * Resolved at the earliest `plugins_loaded` priority, so hook it from a mu-plugin.
860 + *
861 + * @since 16.3
862 + *
863 + * @param bool $enabled Whether to initialize the bundled Backup dashboard. Default true.
864 + */
865 + if ( ! apply_filters( 'jetpack_backup_dashboard_enabled', true ) ) {
866 + return;
867 + }
868 +
869 + $backup = 'Automattic\\Jetpack\\Backup\\V0005\\Jetpack_Backup';
870 +
871 + // An older package would take over the connection (see Jetpack_Backup::DEFAULT_INIT_OPTIONS);
872 + // only the standalone plugin ships one that old, and it draws its own menu.
873 + if ( ! class_exists( $backup ) || ! defined( $backup . '::DEFAULT_INIT_OPTIONS' ) ) {
874 + return;
875 + }
876 +
877 + $backup::initialize( array( 'manage_connection' => false ) );
878 + }
879 +
880 + /**
881 + * Whether the bundled Stats v2 dashboard is enabled.
882 + *
883 + * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
884 + * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
885 + * menu alongside the existing Stats UI; it never replaces or hides the
886 + * legacy Stats menu, admin-bar entries, post-list column, or WP dashboard
887 + * widget. The Stats module's tracking is unaffected either way, and Stats v2
888 + * reads what that module collects, so while the module is off the plugin
889 + * answers false here before even reading the flag.
890 + *
891 + * The package has to be loadable for this to be true, so a site with the
892 + * flag on but a missing package answers false here and never adds the
893 + * Stats v2 menu (a warning is logged instead).
894 + *
895 + * @since 16.1
896 + *
897 + * @return bool
898 + */
899 + public static function is_premium_analytics_enabled() {
900 + if ( null !== self::$premium_analytics_enabled ) {
901 + return self::$premium_analytics_enabled;
902 + }
903 +
904 + if ( ! self::is_module_active( 'stats' ) ) {
905 + self::$premium_analytics_enabled = false;
906 + return false;
907 + }
908 +
909 + /**
910 + * Filters whether the bundled Premium Analytics dashboard is enabled.
911 + *
912 + * Resolved once, from `Jetpack::configure()` on `plugins_loaded`, and only
913 + * while the Stats module is active. Register this from a mu-plugin or a
914 + * plugin's main file — a callback added on `plugins_loaded` or later runs
915 + * too late to be seen.
916 + *
917 + * @since 16.1
918 + *
919 + * @param bool $enabled Defaults to the `jetpack_premium_analytics_enabled` option (false).
920 + */
921 + $flag = (bool) apply_filters( 'jetpack_premium_analytics_enabled', (bool) get_option( 'jetpack_premium_analytics_enabled' ) );
922 +
923 + self::$premium_analytics_enabled = $flag && class_exists( 'Automattic\Jetpack\PremiumAnalytics\Analytics' );
924 +
925 + if ( $flag && ! self::$premium_analytics_enabled ) {
926 + wp_trigger_error(
927 + __METHOD__,
928 + 'The jetpack_premium_analytics_enabled flag is on but the Premium Analytics package is not loadable; keeping the Stats UI in place.'
929 + );
930 + }
931 +
932 + return self::$premium_analytics_enabled;
933 + }
934 +
935 + /**
936 + * Expose the setting that turns the Premium Analytics dashboard on and off.
937 + *
938 + * Deliberately not behind is_premium_analytics_enabled(): this is the setting that flips that
939 + * check, so it has to answer while the dashboard is still off.
940 + *
941 + * @since 16.2
942 + *
943 + * @return void
944 + */
945 + public static function register_premium_analytics_enablement_setting() {
946 + if ( class_exists( 'Automattic\Jetpack\PremiumAnalytics\Enablement_Setting' ) ) {
947 + \Automattic\Jetpack\PremiumAnalytics\Enablement_Setting::register();
948 + }
949 + }
950 +
951 + /**
839 952 * Before everything else starts getting initalized, we need to initialize Jetpack using the
840 953 * Config object.
841 954 */
842 955 public function configure() {
@@ -843,16 +956,12 @@
843 956 $config = new Config();
844 957
845 958 foreach (
846 959 array(
847 - 'blaze',
848 960 'jitm',
849 961 'sync',
962 + 'account_protection',
850 963 'waf',
851 - 'videopress',
852 - 'stats',
853 - 'stats_admin',
854 - 'import',
855 964 )
856 965 as $feature
857 966 ) {
858 967 $config->ensure( $feature );
@@ -857,8 +966,110 @@
857 966 ) {
858 967 $config->ensure( $feature );
859 968 }
860 969
970 + // Enable the VideoPress admin UI (the "Jetpack > VideoPress" dashboard) inside the
971 + // Jetpack plugin, mirroring the standalone Jetpack VideoPress plugin. The dashboard
972 + // only renders when the VideoPress module is active (Status::is_active()); when it
973 + // is not, the menu item links to the My Jetpack interstitial to activate it.
974 + $config->ensure( 'videopress', array( 'admin_ui' => true ) );
975 +
976 + // The Backup dashboard is only an admin menu and REST routes, so it is deferred like Import below.
977 + if ( self::should_eager_load_packages() ) {
978 + self::configure_backup_package();
979 + } else {
980 + add_action( 'rest_api_init', array( __CLASS__, 'configure_backup_package' ), 0 );
981 + }
982 +
983 + /*
984 + * The Import package only registers `jetpack/v4/import` REST routes — it
985 + * does nothing when rendering a front-end page — so gate its `ensure()`
986 + * to keep its PHP out of opcache on the front-end GET hot path. It still
987 + * loads on admin, cron, POST, and WP-CLI requests, and on `rest_api_init`
988 + * for REST: a REST request can't be identified yet at `plugins_loaded`
989 + * (this runs before `Config::on_plugins_loaded`, and `rest_api_init`
990 + * fires later), so it is initialized directly when that hook fires, while
991 + * a plain page view never fires it and so loads nothing.
992 + *
993 + * JITM stays eager (above): unlike Import, its `register()` adds a
994 + * `jetpack_sync_before_send_updated_option` filter that records the
995 + * `jetpack_last_plugin_sync` transient, and a Jetpack Sync send can fire
996 + * on a plain front-end GET — including the dedicated-sync `spawn-sync`
997 + * GET, which runs on `init` and exits before `rest_api_init`. Deferring
998 + * JITM would skip that bookkeeping and leave its message cache stale after
999 + * a plugin change, so it loads on every request as before.
1000 + */
1001 + if ( self::should_eager_load_packages() ) {
1002 + $config->ensure( 'import' );
1003 + } else {
1004 + add_action(
1005 + 'rest_api_init',
1006 + array( __CLASS__, 'configure_import_package' ),
1007 + 0
1008 + );
1009 + }
1010 +
1011 + /*
1012 + * The Stats and Stats Admin packages only do work when the Stats module
1013 + * is active (the front-end tracking pixel) or on wp-admin, REST, cron,
1014 + * POST, and WP-CLI requests: the Stats dashboard page, the stats /
1015 + * stats-app REST endpoints (which the block editor also calls for
1016 + * email-open rates), the transient-cleanup cron, the connection
1017 + * package's package-version tracker (which runs on POSTs and reads the
1018 + * `jetpack_package_versions` filter that Stats registers), and CLI
1019 + * introspection such as the heartbeat inspector. On a plain front-end
1020 + * GET page view with the module off they are inert: the pixel
1021 + * short-circuits on `Stats\Main::should_track()` and every other entry
1022 + * point only hooks rest_api_init, admin, cron, the POST-only tracker, or
1023 + * is reached through WP-CLI.
1024 + * Skip loading them — and eagerly constructing the Stats Admin REST
1025 + * controller — on that hot path to keep their PHP out of opcache, but
1026 + * keep loading them everywhere else exactly as before so the stats REST
1027 + * permission mapping (view_stats, registered by Stats\Main), the
1028 + * editor's stats-app calls, the cleanup cron, and the package-version
1029 + * tracker are all unchanged.
1030 + *
1031 + * REST requests are not yet identifiable here (REST_REQUEST is defined
1032 + * after plugins_loaded), so defer those to rest_api_init. Call the
1033 + * package initializers directly rather than `$config->ensure()`: ensure()
1034 + * only flags a feature for `Config::on_plugins_loaded()` (plugins_loaded
1035 + * priority 2), which has already run by the time rest_api_init fires.
1036 + * Priority 0 runs before each package's own priority-10 route
1037 + * registration, so their routes still register within the same dispatch.
1038 + * A plain page view never fires rest_api_init, so the packages stay
1039 + * unloaded there. See JETPACK-1747.
1040 + */
1041 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'];
1042 + $is_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
1043 +
1044 + if ( self::is_module_active( 'stats' ) || is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli ) {
1045 + $config->ensure( 'stats' );
1046 + $config->ensure( 'stats_admin' );
1047 + } else {
1048 + add_action(
1049 + 'rest_api_init',
1050 + static function () {
1051 + if ( class_exists( 'Automattic\Jetpack\Stats\Main' ) ) {
1052 + \Automattic\Jetpack\Stats\Main::init();
1053 + }
1054 + if ( class_exists( 'Automattic\Jetpack\Stats_Admin\Main' ) ) {
1055 + \Automattic\Jetpack\Stats_Admin\Main::init();
1056 + }
1057 + },
1058 + 0
1059 + );
1060 + }
1061 +
1062 + // Stats v2 (WOOA7S-1595). Unlike Stats above it cannot be deferred when enabled — see
1063 + // Analytics::init() for why, and for why it takes no menu_title here.
1064 + if ( self::is_premium_analytics_enabled() ) {
1065 + \Automattic\Jetpack\PremiumAnalytics\Analytics::init();
1066 + }
1067 +
1068 + // Outside the check above on purpose — see Enablement_Setting. Deferred like Stats, to keep
1069 + // the autoload off the front-end hot path.
1070 + add_action( 'rest_api_init', array( __CLASS__, 'register_premium_analytics_enablement_setting' ), 0 );
1071 +
861 1072 $config->ensure(
862 1073 'connection',
863 1074 array(
864 1075 'slug' => 'jetpack',
@@ -876,12 +1087,8 @@
876 1087 );
877 1088
878 1089 $config->ensure( 'search' );
879 1090
880 - if ( defined( 'ENABLE_WORDADS_SHARED_UI' ) && ENABLE_WORDADS_SHARED_UI ) {
881 - $config->ensure( 'wordads' );
882 - }
883 -
884 1091 if ( ! $this->connection_manager ) {
885 1092 $this->connection_manager = new Connection_Manager( 'jetpack' );
886 1093 }
887 1094
@@ -889,8 +1096,10 @@
889 1096 if ( $modules->is_active( 'publicize' ) && $this->connection_manager->has_connected_user() ) {
890 1097 $config->ensure( 'publicize' );
891 1098 }
892 1099
1100 + add_action( 'jetpack_initialize_tracking', array( $this, 'initialize_tracking' ) );
1101 +
893 1102 /*
894 1103 * Load things that should only be in Network Admin.
895 1104 *
896 1105 * For now blow away everything else until a more full
@@ -905,8 +1114,9 @@
905 1114 $is_connection_ready = self::is_connection_ready();
906 1115
907 1116 if ( $is_connection_ready ) {
908 1117 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
1118 + $this->run_initialize_tracking_action();
909 1119
910 1120 Jetpack_Heartbeat::init();
911 1121 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
912 1122 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
@@ -911,8 +1121,19 @@
911 1121 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
912 1122 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
913 1123 Jetpack_Search_Performance_Logger::init();
914 1124 }
1125 + } else {
1126 + add_action( 'jetpack_agreed_to_terms_of_service', array( $this, 'run_initialize_tracking_action' ) );
1127 + add_action( 'rest_api_init', array( $this, 'run_initialize_tracking_action' ) );
1128 + add_filter(
1129 + 'xmlrpc_methods',
1130 + function ( $methods ) {
1131 + $this->run_initialize_tracking_action();
1132 + return $methods;
1133 + },
1134 + 1
1135 + );
915 1136 }
916 1137
917 1138 // Initialize remote file upload request handlers.
918 1139 $this->add_remote_request_handlers();
@@ -922,20 +1143,10 @@
922 1143 */
923 1144 if ( $is_connection_ready ) {
924 1145 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
925 1146 add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
926 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
927 - add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
1147 + add_action( 'rest_api_init', array( $this, 'maybe_initialize_rest_jsonapi' ) );
928 1148 }
929 -
930 - if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
931 - add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
932 - } else {
933 - /**
934 - * Initialize tracking right after the user agrees to the terms of service.
935 - */
936 - add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
937 - }
938 1149 }
939 1150
940 1151 /**
941 1152 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
@@ -943,13 +1154,58 @@
943 1154 *
944 1155 * @action plugins_loaded
945 1156 */
946 1157 public function late_initialization() {
947 - add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
1158 + add_action( 'after_setup_theme', array( 'Jetpack', 'load_modules' ), -2 );
948 1159
949 - Partner::init();
950 - My_Jetpack_Initializer::init();
1160 + /*
1161 + * My Jetpack is a wp-admin dashboard. Its Initializer::init() only wires
1162 + * up admin-menu, admin_init, and rest_api_init surfaces — and eagerly
1163 + * loads every product class (backup, boost, protect, …) just to register
1164 + * admin plugin-action links — so none of it is needed on a plain
1165 + * front-end GET page view. (The pieces that do immediate work, e.g.
1166 + * Connection REST authentication and Licensing, are already initialized
1167 + * unconditionally in Jetpack's constructor, so they are unaffected here.)
1168 + *
1169 + * Gate the call to the request types where My Jetpack actually does work.
1170 + * REST can't be detected yet at plugins_loaded, so initialize on
1171 + * rest_api_init for that branch; a plain page view never fires it, so My
1172 + * Jetpack stays unloaded there.
1173 + */
1174 + if ( self::should_eager_load_packages() ) {
1175 + My_Jetpack_Initializer::init();
1176 + } else {
1177 + add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1178 + }
951 1179
1180 + Scan_Page_Init::initialize();
1181 + Jetpack_SEO_Initializer::init();
1182 +
1183 + if ( ( new Modules() )->is_active( 'podcast' ) ) {
1184 + Podcast::init();
1185 + }
1186 +
1187 + /*
1188 + * Initialize Boost Speed Score. It only does work on REST requests (the
1189 + * dashboard speed-score endpoints) and on a few Jetpack Boost lifecycle
1190 + * actions, so defer constructing it — and loading the boost-speed-score
1191 + * package classes — until one of those hooks actually fires instead of on
1192 + * every request. Priority 0 ensures the object's own callbacks (added in
1193 + * its constructor at the default priority) still run for the firing hook.
1194 + */
1195 + $initialize_speed_score = static function () {
1196 + static $initialized = false;
1197 + if ( $initialized ) {
1198 + return;
1199 + }
1200 + $initialized = true;
1201 + new Speed_Score( array(), 'jetpack-dashboard' );
1202 + };
1203 + add_action( 'rest_api_init', $initialize_speed_score, 0 );
1204 + add_action( 'jetpack_boost_deactivate', $initialize_speed_score, 0 );
1205 + add_action( 'jetpack_boost_environment_changed', $initialize_speed_score, 0 );
1206 + add_action( 'handle_environment_change', $initialize_speed_score, 0 );
1207 +
952 1208 /**
953 1209 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
954 1210 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
955 1211 *
@@ -987,8 +1243,10 @@
987 1243
988 1244 /**
989 1245 * Redirect edit post links to Calypso.
990 1246 *
1247 + * @deprecated since 13.9
1248 + *
991 1249 * @param string $default_url Post edit URL.
992 1250 * @param int $post_id Post ID.
993 1251 *
994 1252 * @return string
@@ -993,8 +1251,10 @@
993 1251 *
994 1252 * @return string
995 1253 */
996 1254 public function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1255 + _deprecated_function( __METHOD__, '13.9' );
1256 +
997 1257 $post = get_post( $post_id );
998 1258
999 1259 if ( empty( $post ) ) {
1000 1260 return $default_url;
@@ -1025,13 +1285,17 @@
1025 1285
1026 1286 /**
1027 1287 * Redirect edit comment links to Calypso.
1028 1288 *
1289 + * @deprecated since 13.9
1290 + *
1029 1291 * @param string $url Comment edit URL.
1030 1292 *
1031 1293 * @return string
1032 1294 */
1033 1295 public function point_edit_comment_links_to_calypso( $url ) {
1296 + _deprecated_function( __METHOD__, '13.9' );
1297 +
1034 1298 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1035 1299 $query_args = null;
1036 1300 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1037 1301
@@ -1050,30 +1314,16 @@
1050 1314 *
1051 1315 * @return array list of callables.
1052 1316 */
1053 1317 public function filter_sync_callable_whitelist( $callables ) {
1054 -
1055 1318 // Jetpack Functions.
1056 1319 $jetpack_callables = array(
1057 1320 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1058 1321 'updates' => array( 'Jetpack', 'get_updates' ),
1059 1322 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1323 + 'theme_styles' => array( 'Automattic\\Jetpack\\Plugin\\Theme_Styles_Sync', 'get_theme_styles' ),
1060 1324 );
1061 - $callables = array_merge( $callables, $jetpack_callables );
1062 -
1063 - // Jetpack_SSO_Helpers.
1064 - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1065 - $sso_helpers = array(
1066 - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1067 - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1068 - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1069 - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1070 - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1071 - );
1072 - $callables = array_merge( $callables, $sso_helpers );
1073 - }
1074 -
1075 - return $callables;
1325 + return array_merge( $callables, $jetpack_callables );
1076 1326 }
1077 1327
1078 1328 /**
1079 1329 * Extend Sync multisite callables with Jetpack Plugin functions.
@@ -1098,41 +1348,8 @@
1098 1348 return $callables;
1099 1349 }
1100 1350
1101 1351 /**
1102 - * Deprecated
1103 - * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1104 - *
1105 - * @param array $params The action parameters.
1106 - *
1107 - * @deprecated since 9.8.
1108 - */
1109 - public function jetpack_track_last_sync_callback( $params ) {
1110 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1111 - return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1112 - }
1113 -
1114 - /**
1115 - * Jetpack Connection banner callback function.
1116 - *
1117 - * @return void
1118 - */
1119 - public function jetpack_connection_banner_callback() {
1120 - check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1121 -
1122 - // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1123 - if (
1124 - isset( $_REQUEST['dismissBanner'] ) &&
1125 - ! Jetpack_Connection_Banner::force_display()
1126 - ) {
1127 - Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1128 - wp_send_json_success();
1129 - }
1130 -
1131 - wp_die();
1132 - }
1133 -
1134 - /**
1135 1352 * If there are any stats that need to be pushed, but haven't been, push them now.
1136 1353 */
1137 1354 public function push_stats() {
1138 1355 if ( ! empty( $this->stats ) ) {
@@ -1147,16 +1364,8 @@
1147 1364 * @param string $cap Capability name.
1148 1365 */
1149 1366 public function jetpack_custom_caps( $caps, $cap ) {
1150 1367 switch ( $cap ) {
1151 - case 'jetpack_manage_modules':
1152 - case 'jetpack_activate_modules':
1153 - case 'jetpack_deactivate_modules':
1154 - $caps = array( 'manage_options' );
1155 - break;
1156 - case 'jetpack_configure_modules':
1157 - $caps = array( 'manage_options' );
1158 - break;
1159 1368 case 'jetpack_manage_autoupdates':
1160 1369 $caps = array(
1161 1370 'manage_options',
1162 1371 'update_plugins',
@@ -1174,9 +1383,9 @@
1174 1383 if ( $is_offline_mode ) {
1175 1384 $caps = array( 'manage_options' );
1176 1385 break;
1177 1386 } else {
1178 - $caps = array( 'read' );
1387 + $caps = array( 'edit_posts' );
1179 1388 }
1180 1389 break;
1181 1390 }
1182 1391 return $caps;
@@ -1335,9 +1544,9 @@
1335 1544 }
1336 1545 /**
1337 1546 * Does the network allow admins to add new users.
1338 1547 *
1339 - * @return boolian
1548 + * @return bool
1340 1549 */
1341 1550 public static function network_add_new_users() {
1342 1551 return (bool) get_site_option( 'add_new_users' );
1343 1552 }
@@ -1344,9 +1553,9 @@
1344 1553 /**
1345 1554 * File upload psace left per site in MB.
1346 1555 * -1 means NO LIMIT.
1347 1556 *
1348 - * @return number
1557 + * @return int
1349 1558 */
1350 1559 public static function network_site_upload_space() {
1351 1560 // value in MB.
1352 1561 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
@@ -1363,9 +1572,9 @@
1363 1572
1364 1573 /**
1365 1574 * Maximum file upload size set by the network.
1366 1575 *
1367 - * @return number
1576 + * @return int
1368 1577 */
1369 1578 public static function network_max_upload_file_size() {
1370 1579 // value in KB.
1371 1580 return get_site_option( 'fileupload_maxk', 300 );
@@ -1524,9 +1733,9 @@
1524 1733 }
1525 1734 return 0;
1526 1735 }
1527 1736
1528 -// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1737 + // phpcs:disable WordPress.WP.CapitalPDangit.MisspelledInComment
1529 1738 /**
1530 1739 * Gets updates and stores in jetpack_updates.
1531 1740 *
1532 1741 * The jetpack_updates option is saved in the following schema:
@@ -1542,8 +1751,9 @@
1542 1751 *
1543 1752 * @return array
1544 1753 */
1545 1754 public static function get_updates() {
1755 + $updates = array();
1546 1756 $update_data = wp_get_update_data();
1547 1757
1548 1758 // Stores the individual update counts as well as the total count.
1549 1759 if ( isset( $update_data['counts'] ) ) {
@@ -1556,11 +1766,11 @@
1556 1766 if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1557 1767 $updates['wp_update_version'] = $cur->current;
1558 1768 }
1559 1769 }
1560 - return isset( $updates ) ? $updates : array();
1770 + return $updates;
1561 1771 }
1562 - // phpcs:enable
1772 + // phpcs:enable WordPress.WP.CapitalPDangit.MisspelledInComment
1563 1773
1564 1774 /**
1565 1775 * Get update details for core, plugins, and themes.
1566 1776 *
@@ -1618,44 +1828,8 @@
1618 1828 return apply_filters( 'jetpack_is_connection_ready', self::connection()->is_connected(), self::connection() );
1619 1829 }
1620 1830
1621 1831 /**
1622 - * Deprecated: Is Jetpack in development (offline) mode?
1623 - *
1624 - * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1625 - * and themes still use it, and we do not want to flood them with notices.
1626 - *
1627 - * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1628 - *
1629 - * @deprecated since 8.0.
1630 - */
1631 - public static function is_development_mode() {
1632 - _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1633 - return ( new Status() )->is_offline_mode();
1634 - }
1635 -
1636 - /**
1637 - * Whether the site is currently onboarding or not.
1638 - * A site is considered as being onboarded if it currently has an onboarding token.
1639 - *
1640 - * @since 5.8
1641 - * @deprecated Use \Automattic\Jetpack\Status()->is_onboarding()
1642 - *
1643 - * @access public
1644 - * @static
1645 - *
1646 - * @return bool True if the site is currently onboarding, false otherwise
1647 - */
1648 - public static function is_onboarding() {
1649 - _deprecated_function( __METHOD__, 'jetpack-10.9', 'Automattic\\Jetpack\\Status\\is_onboarding' );
1650 -
1651 - if ( ! method_exists( 'Automattic\Jetpack\Status', 'is_onboarding' ) ) {
1652 - return Jetpack_Options::get_option( 'onboarding' ) !== false;
1653 - }
1654 - return ( new Status() )->is_onboarding();
1655 - }
1656 -
1657 - /**
1658 1832 * Determines reason for Jetpack offline mode.
1659 1833 */
1660 1834 public static function development_mode_trigger_text() {
1661 1835 $status = new Status();
@@ -1669,11 +1843,10 @@
1669 1843 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1670 1844 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1671 1845 } elseif ( $status->is_local_site() ) {
1672 1846 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1673 - /** This filter is documented in packages/status/src/class-status.php */
1674 - } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1675 - $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1847 + } elseif ( get_option( 'jetpack_offline_mode' ) ) {
1848 + $notice = __( 'The jetpack_offline_mode option is set to true.', 'jetpack' );
1676 1849 } else {
1677 1850 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1678 1851 }
1679 1852
@@ -1688,9 +1861,9 @@
1688 1861 if ( ( new Status() )->is_offline_mode() ) {
1689 1862 $notice = sprintf(
1690 1863 /* translators: %s is a URL */
1691 1864 __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1692 - Redirect::get_url( 'jetpack-support-development-mode' )
1865 + esc_url( Redirect::get_url( 'jetpack-support-development-mode' ) )
1693 1866 );
1694 1867
1695 1868 $notice .= ' ' . self::development_mode_trigger_text();
1696 1869
@@ -1699,19 +1872,12 @@
1699 1872
1700 1873 // Throw up a notice if using a development version and as for feedback.
1701 1874 if ( self::is_development_version() ) {
1702 1875 /* translators: %s is a URL */
1703 - $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), Redirect::get_url( 'jetpack-contact-support-beta-group' ) );
1876 + $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), esc_url( Redirect::get_url( 'jetpack-contact-support-beta-group' ) ) );
1704 1877
1705 1878 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1706 1879 }
1707 - // Throw up a notice if using staging mode.
1708 - if ( ( new Status() )->is_staging_site() ) {
1709 - /* translators: %s is a URL */
1710 - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1711 -
1712 - echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1713 - }
1714 1880 }
1715 1881
1716 1882 /**
1717 1883 * Whether Jetpack's version maps to a public release, or a development version.
@@ -1732,28 +1898,8 @@
1732 1898 );
1733 1899 }
1734 1900
1735 1901 /**
1736 - * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1737 - *
1738 - * @param int $user_id User ID or will use get_current_user_id if false/not provided.
1739 - */
1740 - public static function is_user_connected( $user_id = false ) {
1741 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1742 - return self::connection()->is_user_connected( $user_id );
1743 - }
1744 -
1745 - /**
1746 - * Get the wpcom user data of the current|specified connected user.
1747 - *
1748 - * @param null|int $user_id User ID or will use get_current_user_id if null.
1749 - */
1750 - public static function get_connected_user_data( $user_id = null ) {
1751 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1752 - return self::connection()->get_connected_user_data( $user_id );
1753 - }
1754 -
1755 - /**
1756 1902 * Get the wpcom email of the current|specified connected user.
1757 1903 *
1758 1904 * @param null|int $user_id User ID or will use get_current_user_id if null.
1759 1905 */
@@ -1805,18 +1951,11 @@
1805 1951 */
1806 1952 public static function load_modules() {
1807 1953 $status = new Status();
1808 1954
1809 - if ( method_exists( $status, 'is_onboarding' ) ) {
1810 - $is_onboarding = $status->is_onboarding();
1811 - } else {
1812 - $is_onboarding = self::is_onboarding();
1813 - }
1814 -
1815 1955 if (
1816 1956 ! self::is_connection_ready()
1817 1957 && ! $status->is_offline_mode()
1818 - && ! $is_onboarding
1819 1958 && (
1820 1959 ! is_multisite()
1821 1960 || ! get_site_option( 'jetpack_protect_active' )
1822 1961 )
@@ -1937,22 +2076,14 @@
1937 2076 *
1938 2077 * @todo Store the result in core's object cache maybe?
1939 2078 */
1940 2079 public static function get_active_plugins() {
1941 - $active_plugins = (array) get_option( 'active_plugins', array() );
1942 -
1943 - if ( is_multisite() ) {
1944 - // Due to legacy code, active_sitewide_plugins stores them in the keys,
1945 - // whereas active_plugins stores them in the values.
1946 - $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1947 - if ( $network_plugins ) {
1948 - $active_plugins = array_merge( $active_plugins, $network_plugins );
1949 - }
2080 + // Older Connection copies can load first and lack this method.
2081 + if ( ! method_exists( Heartbeat::class, 'get_active_plugins' ) ) {
2082 + return array();
1950 2083 }
1951 2084
1952 - sort( $active_plugins );
1953 -
1954 - return array_unique( $active_plugins );
2085 + return Heartbeat::get_active_plugins();
1955 2086 }
1956 2087
1957 2088 /**
1958 2089 * Gets and parses additional plugin data to send with the heartbeat data
@@ -2090,8 +2221,10 @@
2090 2221 *
2091 2222 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2092 2223 */
2093 2224 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2225 + // @todo Remove this require once the deprecated Jetpack_Twitter_Cards wrapper has been removed.
2226 + // Twitter Cards functionality now lives in the jetpack-post-media package (Automattic\Jetpack\Post_Media\Twitter_Cards).
2094 2227 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2095 2228 }
2096 2229 }
2097 2230
@@ -2194,9 +2327,9 @@
2194 2327 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2195 2328 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2196 2329 }
2197 2330 wp_safe_redirect( self::admin_url( 'page=' . rawurlencode( $page ) ) );
2198 - exit;
2331 + exit( 0 );
2199 2332 }
2200 2333 }
2201 2334
2202 2335 /**
@@ -2243,8 +2376,12 @@
2243 2376 default:
2244 2377 break;
2245 2378 }
2246 2379 }
2380 + if ( method_exists( Feature_Policy::class, 'ensure_hooks' ) ) {
2381 + Feature_Policy::ensure_hooks();
2382 + }
2383 +
2247 2384 /**
2248 2385 * Filters the array of default modules.
2249 2386 *
2250 2387 * @since 2.5.0
@@ -2276,12 +2413,14 @@
2276 2413 * @return array
2277 2414 */
2278 2415 public function handle_deprecated_modules( $modules ) {
2279 2416 $deprecated_modules = array(
2280 - 'debug' => null, // Closed out and moved to the debugger library.
2281 - 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2282 - 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2283 - 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2417 + 'debug' => null, // Closed out and moved to the debugger library.
2418 + 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2419 + 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2420 + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2421 + 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2422 + 'enhanced-distribution' => null, // Closed out in 13.3 -- WP.com is winding down the firehose.
2284 2423 );
2285 2424
2286 2425 // Don't activate SSO if they never completed activating WPCC.
2287 2426 if ( self::is_module_active( 'wpcc' ) ) {
@@ -2335,8 +2474,17 @@
2335 2474 }
2336 2475 }
2337 2476 }
2338 2477
2478 + // Special case to convert block setting to a block module.
2479 + $block_key = array_search( 'blocks', $modules, true );
2480 + if ( $block_key !== false ) { // Only care if 'blocks' made it through the previous filters.
2481 + $block_option = get_option( 'jetpack_blocks_disabled', null );
2482 + if ( $block_option ) {
2483 + unset( $modules[ $block_key ] );
2484 + }
2485 + }
2486 +
2339 2487 return $modules;
2340 2488 }
2341 2489
2342 2490 /**
@@ -2393,23 +2541,30 @@
2393 2541
2394 2542 /**
2395 2543 * Return module name translation. Uses matching string created in modules/module-headings.php.
2396 2544 *
2545 + * The module list is globbed from `modules/` at runtime, so a module can be listed with no
2546 + * entry in that generated file. Fall back to the untranslated header rather than overwriting
2547 + * it with the null `jetpack_get_module_i18n()` returns for an unknown slug.
2548 + *
2397 2549 * @since 3.9.2
2398 2550 *
2399 2551 * @param array $modules Array of Jetpack modules.
2400 2552 *
2401 - * @return string|void
2553 + * @return array
2402 2554 */
2403 2555 public static function get_translated_modules( $modules ) {
2404 2556 foreach ( $modules as $index => $module ) {
2405 2557 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2406 - if ( isset( $module['name'] ) ) {
2407 - $modules[ $index ]['name'] = $i18n_module['name'];
2558 + $name = $i18n_module['name'] ?? null;
2559 + $description = $i18n_module['description'] ?? null;
2560 +
2561 + if ( null !== $name && isset( $module['name'] ) ) {
2562 + $modules[ $index ]['name'] = $name;
2408 2563 }
2409 - if ( isset( $module['description'] ) ) {
2410 - $modules[ $index ]['description'] = $i18n_module['description'];
2411 - $modules[ $index ]['short_description'] = $i18n_module['description'];
2564 + if ( null !== $description && isset( $module['description'] ) ) {
2565 + $modules[ $index ]['description'] = $description;
2566 + $modules[ $index ]['short_description'] = $description;
2412 2567 }
2413 2568 if ( isset( $module['module_tags'] ) ) {
2414 2569 $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2415 2570 }
@@ -2449,20 +2604,28 @@
2449 2604
2450 2605 /**
2451 2606 * Catches PHP errors. Must be used in conjunction with output buffering.
2452 2607 *
2608 + * @deprecated since 13.5
2453 2609 * @param bool $catch True to start catching, False to stop.
2454 2610 *
2455 2611 * @static
2612 + * @deprecated 13.5
2613 + * @see \Automattic\Jetpack\Errors
2456 2614 */
2457 2615 public static function catch_errors( $catch ) {
2616 + _deprecated_function( __METHOD__, '13.5' );
2617 + // @phan-suppress-next-line PhanDeprecatedClass
2458 2618 return ( new Errors() )->catch_errors( $catch );
2459 2619 }
2460 2620
2461 2621 /**
2462 2622 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2623 + *
2624 + * @deprecated since 13.5
2463 2625 */
2464 2626 public static function catch_errors_on_shutdown() {
2627 + _deprecated_function( __METHOD__, '13.5' );
2465 2628 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2466 2629 }
2467 2630
2468 2631 /**
@@ -2554,9 +2717,9 @@
2554 2717 }
2555 2718
2556 2719 if ( $deactivated ) {
2557 2720 if ( $send_state_messages ) {
2558 - self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2721 + self::state( 'deactivated_plugins', implode( ',', $deactivated ) );
2559 2722 }
2560 2723
2561 2724 if ( $redirect ) {
2562 2725 $url = add_query_arg(
@@ -2566,9 +2729,9 @@
2566 2729 ),
2567 2730 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2568 2731 );
2569 2732 wp_safe_redirect( $url );
2570 - exit;
2733 + exit( 0 );
2571 2734 }
2572 2735 }
2573 2736
2574 2737 /**
@@ -2586,9 +2749,8 @@
2586 2749
2587 2750 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating.
2588 2751 if ( $send_state_messages ) {
2589 2752 self::restate();
2590 - self::catch_errors( true );
2591 2753 }
2592 2754
2593 2755 $active = self::get_active_modules();
2594 2756
@@ -2656,10 +2818,8 @@
2656 2818 if ( $send_state_messages ) {
2657 2819 self::state( 'error', false );
2658 2820 self::state( 'module', false );
2659 2821 }
2660 -
2661 - self::catch_errors( false );
2662 2822 /**
2663 2823 * Fires when default modules are activated.
2664 2824 *
2665 2825 * @since 1.9.0
@@ -2717,9 +2877,9 @@
2717 2877 * @return string $url module configuration URL.
2718 2878 */
2719 2879 public static function module_configuration_url( $module ) {
2720 2880 $module = self::get_module_slug( $module );
2721 - $default_url = self::admin_url() . "#/settings?term=$module";
2881 + $default_url = self::admin_url( array( 'page' => 'jetpack-settings' ) ) . "#/settings?term=$module";
2722 2882 /**
2723 2883 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
2724 2884 *
2725 2885 * @since 6.9.0
@@ -2737,9 +2897,9 @@
2737 2897 *
2738 2898 * @param string $message Error message.
2739 2899 * @param bool $deactivate Deactivate Jetpack or not.
2740 2900 *
2741 - * @return void
2901 + * @return never
2742 2902 */
2743 2903 public static function bail_on_activation( $message, $deactivate = true ) {
2744 2904 ?>
2745 2905 <!doctype html>
@@ -2777,9 +2937,9 @@
2777 2937 if ( $update ) {
2778 2938 update_option( 'active_plugins', array_filter( $plugins ) );
2779 2939 }
2780 2940 }
2781 - exit;
2941 + exit( 0 );
2782 2942 }
2783 2943
2784 2944 /**
2785 2945 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
@@ -2804,12 +2964,18 @@
2804 2964 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
2805 2965
2806 2966 Health::on_jetpack_activated();
2807 2967
2968 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
2969 +
2808 2970 if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) {
2809 2971 Sync_Actions::do_only_first_initial_sync();
2810 2972 }
2811 2973
2974 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
2975 + Woocommerce_Analytics::maybe_add_proxy_speed_module();
2976 + }
2977 +
2812 2978 self::plugin_initialize();
2813 2979 }
2814 2980
2815 2981 /**
@@ -2844,9 +3010,9 @@
2844 3010
2845 3011 if ( $plugins_path === $referer['path'] ) {
2846 3012 $source_type = 'list';
2847 3013 } elseif ( $plugins_install_path === $referer['path'] ) {
2848 - $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3014 + $tab = $query_parts['tab'] ?? 'featured';
2849 3015 switch ( $tab ) {
2850 3016 case 'popular':
2851 3017 $source_type = 'popular';
2852 3018 break;
@@ -2856,10 +3022,10 @@
2856 3022 case 'favorites':
2857 3023 $source_type = 'favorites';
2858 3024 break;
2859 3025 case 'search':
2860 - $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
2861 - $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3026 + $source_type = 'search-' . ( $query_parts['type'] ?? 'term' );
3027 + $source_query = $query_parts['s'] ?? null;
2862 3028 break;
2863 3029 default:
2864 3030 $source_type = 'featured';
2865 3031 }
@@ -2868,29 +3034,171 @@
2868 3034 return array( $source_type, $source_query );
2869 3035 }
2870 3036
2871 3037 /**
2872 - * Runs before bumping version numbers up to a new version
3038 + * Runs before bumping version numbers up to a new version.
2873 3039 *
2874 - * @param string $version Version:timestamp.
3040 + * Only ever registered the hooks for the release post update modal, which has been removed.
3041 + * No longer hooked to `updating_jetpack_version`.
3042 + *
3043 + * @deprecated 16.2
3044 + *
3045 + * @param string $version Version:timestamp.
2875 3046 * @param string $old_version Old Version:timestamp or false if not set yet.
2876 3047 */
2877 - public static function do_version_bump( $version, $old_version ) {
2878 - if ( $old_version ) { // For existing Jetpack installations.
2879 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3048 + public static function do_version_bump( $version, $old_version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Signature preserved for the deprecation shim.
3049 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
3050 + }
2880 3051
2881 - // If a front end page is visited after the update, the 'wp' action will fire.
2882 - add_action( 'wp', 'Jetpack::set_update_modal_display' );
3052 + /**
3053 + * Enables the Newsletter (subscriptions) module for existing sites now that it is a default-on module.
3054 + *
3055 + * Fresh installs receive the module via its "Auto Activate: Yes" header, so this only handles sites
3056 + * upgrading from a version where the module defaulted off. It runs once per site (guarded by the
3057 + * subscriptions_default_on_migrated option). Fresh installs are marked as migrated immediately so the
3058 + * migration never runs for them. After it has run, the user's choice to deactivate the module again
3059 + * (for example from the My Jetpack Products page) is respected and never reverted.
3060 + *
3061 + * The module requires a connection, so on a disconnected site the migration is deferred without setting
3062 + * the guard, allowing a later version bump to retry once the site is connected.
3063 + *
3064 + * @param string $version New Jetpack version:timestamp.
3065 + * @param string|false $old_version Previous Jetpack version:timestamp, or false on a fresh install.
3066 + */
3067 + public static function activate_subscriptions_module_for_existing_sites( $version, $old_version ) {
3068 + if ( get_option( 'jetpack_subscriptions_default_on_migrated' ) ) {
3069 + return;
3070 + }
2883 3071
2884 - // If an admin page is visited after the update, the 'current_screen' action will fire.
2885 - add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3072 + // Fresh installs get the module via its "Auto Activate: Yes" header. Mark them as migrated so a
3073 + // later opt-out is never reverted by the existing-site path on a subsequent version bump.
3074 + if ( ! $old_version ) {
3075 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3076 + return;
2886 3077 }
3078 +
3079 + if ( ! self::is_connection_ready() ) {
3080 + return;
3081 + }
3082 +
3083 + // Mark as migrated only once the module is active, so a transient activation failure is retried on
3084 + // a later version bump rather than being silently skipped.
3085 + if ( self::is_module_active( 'subscriptions' ) || self::activate_module( 'subscriptions', false, false ) ) {
3086 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3087 + }
2887 3088 }
2888 3089
2889 3090 /**
3091 + * Option flag that records the AI master-switch opt-out reconciliation has run,
3092 + * so it never runs twice.
3093 + *
3094 + * @var string
3095 + */
3096 + const AI_MASTER_OPTOUT_MIGRATED_OPTION = 'jetpack_ai_master_optout_migrated';
3097 +
3098 + /**
3099 + * Register the on-upgrade init hooks whose relative ORDER matters, extracted so
3100 + * the ordering can be asserted in tests without invoking plugin_upgrade() (whose
3101 + * guards make it unreliable to trigger under test). activate_new_modules()
3102 + * (init, default priority 10) auto-activates "Auto Activate: Yes" modules
3103 + * including the `ai` master; reconcile_ai_master_optout() must run at a LATER
3104 + * priority to honor an explicit AI opt-out.
3105 + *
3106 + * @return void
3107 + */
3108 + public static function register_upgrade_init_hooks() {
3109 + add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
3110 + add_action( 'init', array( __CLASS__, 'reconcile_ai_master_optout' ), 20 );
3111 + }
3112 +
3113 + /**
3114 + * Preserves an explicit Jetpack AI opt-out when the `ai` module becomes the site-wide
3115 + * master switch off WordPress.com Simple (self-hosted and Atomic).
3116 + *
3117 + * The `ai` module is "Auto Activate: Yes", so on upgrade {@see self::activate_new_modules()}
3118 + * turns it on for connected sites — the desired default-on / auto-enable-on-connection
3119 + * behavior, which this method deliberately leaves alone. The one case it corrects is a site
3120 + * that had explicitly disabled Jetpack AI (the `jetpack_ai_enabled` option present and falsey)
3121 + * before the module shipped: that opt-out must survive the module becoming the master, so the
3122 + * module is deactivated for exactly those sites. An absent or truthy option is left untouched.
3123 + *
3124 + * Ordering is the whole point. `activate_new_modules()` is hooked on `init` at priority 10 and
3125 + * auto-activates the module there; this method is hooked on `init` at priority 20 (see
3126 + * {@see self::plugin_upgrade()}), so it runs AFTER the auto-activation and its deactivation is
3127 + * the final state. A version-guarded block that ran inline during `plugins_loaded` would be
3128 + * undone by the later auto-activation, which is why this is a late-init hook rather than an
3129 + * inline upgrade step.
3130 + *
3131 + * WordPress.com Simple never runs modules — the option stays the master there — so this is a
3132 + * no-op on Simple. The {@see self::AI_MASTER_OPTOUT_MIGRATED_OPTION} flag makes it run exactly
3133 + * once, which matters because off-Simple the option is no longer the master after this runs:
3134 + * a stale falsey option must not keep re-deactivating a module the user later turns back on.
3135 + *
3136 + * @return void
3137 + */
3138 + public static function reconcile_ai_master_optout() {
3139 + if ( get_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION ) ) {
3140 + return;
3141 + }
3142 +
3143 + // Simple keeps the `jetpack_ai_enabled` option as the master; modules don't run there.
3144 + if ( ( new Host() )->is_wpcom_simple() ) {
3145 + return;
3146 + }
3147 +
3148 + // A sentinel default distinguishes an absent option (leave auto-activation alone) from one
3149 + // explicitly stored falsey (an opt-out to preserve).
3150 + $stored = get_option( 'jetpack_ai_enabled', 'not-set' );
3151 + if ( 'not-set' !== $stored && ! (bool) $stored ) {
3152 + ( new Modules() )->deactivate( 'ai' );
3153 + }
3154 +
3155 + update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3156 + }
3157 +
3158 + /**
3159 + * Deletes obsolete SEO module-state options without changing module activation.
3160 + *
3161 + * @since 16.3
3162 + */
3163 + public static function cleanup_seo_module_state_options() {
3164 + delete_option( 'jetpack_seo_sitemap_enabled' );
3165 + delete_option( 'jetpack_seo_canonical_urls_enabled' );
3166 + delete_option( 'jetpack_seo_module_state_reconciled' );
3167 + }
3168 +
3169 + /**
3170 + * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3171 + * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3172 + *
3173 + * Hooked on `updating_jetpack_version`, which fires on every install including the
3174 + * first — with `$old_version === false` on a brand-new site (the same signal
3175 + * {@see self::activate_subscriptions_module_for_existing_sites()} keys off). Fresh
3176 + * installs are seeded visible; existing installs are seeded hidden and opt in later
3177 + * via the legacy Traffic page or My Jetpack. `add_option()` makes this seed-once: it
3178 + * never overrides a value a later opt-in (or opt-out) has set. WordPress.com sites
3179 + * ignore this option entirely (always visible) — see
3180 + * {@see \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()}.
3181 + *
3182 + * @param string $version The new Jetpack version (unused).
3183 + * @param string|false $old_version The previous version, or false on a fresh install.
3184 + */
3185 + public static function seed_seo_visibility_cohort( $version, $old_version ) {
3186 + add_option( Jetpack_SEO_Initializer::VISIBILITY_OPTION, ! $old_version );
3187 + }
3188 +
3189 + /**
2890 3190 * Sets the display_update_modal state.
3191 + *
3192 + * The release post update modal that read this state has been removed. The write is kept so the
3193 + * method still behaves as documented for the deprecation window. When this is deleted, also drop
3194 + * the matching `display_update_modal` guard in Automattic\Jetpack\CookieState::should_set_cookie(),
3195 + * which exists only to keep this key out of the cookie on the Jetpack admin screen.
3196 + *
3197 + * @deprecated 16.2
2891 3198 */
2892 3199 public static function set_update_modal_display() {
3200 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2893 3201 self::state( 'display_update_modal', true );
2894 3202 }
2895 3203
2896 3204 /**
@@ -2895,11 +3203,16 @@
2895 3203
2896 3204 /**
2897 3205 * Enqueues the block library styles.
2898 3206 *
3207 + * Only ever used by the release post update modal, which has been removed.
3208 + *
3209 + * @deprecated 16.2
3210 + *
2899 3211 * @param string $hook The current admin page.
2900 3212 */
2901 3213 public static function enqueue_block_style( $hook ) {
3214 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2902 3215 if ( 'toplevel_page_jetpack' === $hook ) {
2903 3216 wp_enqueue_style( 'wp-block-library' );
2904 3217 }
2905 3218 }
@@ -2928,9 +3241,8 @@
2928 3241
2929 3242 self::load_modules();
2930 3243
2931 3244 Jetpack_Options::delete_option( 'do_activate' );
2932 - Jetpack_Options::delete_option( 'dismissed_connection_banner' );
2933 3245 }
2934 3246
2935 3247 /**
2936 3248 * Handles the activation of the default modules depending on the current state of the site:
@@ -2989,8 +3301,12 @@
2989 3301 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
2990 3302 self::disconnect();
2991 3303 Jetpack_Options::delete_option( 'version' );
2992 3304 }
3305 +
3306 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
3307 + Woocommerce_Analytics::maybe_remove_proxy_speed_module();
3308 + }
2993 3309 }
2994 3310
2995 3311 /**
2996 3312 * Set activated option to 4 on jetpack_idc_disconnect action.
@@ -3018,9 +3334,9 @@
3018 3334 $connection->remove_connection( ! Identity_Crisis::validate_sync_error_idc_option() );
3019 3335 }
3020 3336
3021 3337 /**
3022 - * Happens after a successfull disconnection.
3338 + * Happens after a successful disconnection.
3023 3339 *
3024 3340 * @static
3025 3341 */
3026 3342 public static function jetpack_site_disconnected() {
@@ -3025,8 +3341,10 @@
3025 3341 */
3026 3342 public static function jetpack_site_disconnected() {
3027 3343 Identity_Crisis::clear_all_idc_options();
3028 3344
3345 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
3346 +
3029 3347 // Delete all the sync related data. Since it could be taking up space.
3030 3348 Sender::get_instance()->uninstall();
3031 3349
3032 3350 /**
@@ -3043,10 +3361,13 @@
3043 3361 }
3044 3362 }
3045 3363
3046 3364 /**
3047 - * Disconnects the user
3365 + * Disconnects the user.
3048 3366 *
3367 + * @deprecated 13.4
3368 + * @see \Automattic\Jetpack\Connection\Manager::disconnect_user()
3369 + *
3049 3370 * @param int $user_id The user ID to disconnect.
3050 3371 */
3051 3372 public function disconnect_user( $user_id ) {
3052 3373 $this->connection_manager->disconnect_user( $user_id );
@@ -3052,21 +3373,8 @@
3052 3373 $this->connection_manager->disconnect_user( $user_id );
3053 3374 }
3054 3375
3055 3376 /**
3056 - * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3057 - *
3058 - * @deprecated since Jetpack 9.7.0
3059 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
3060 - *
3061 - * @return bool|WP_Error
3062 - */
3063 - public static function try_registration() {
3064 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3065 - return static::connection()->try_registration();
3066 - }
3067 -
3068 - /**
3069 3377 * Checking the domain names in beta versions.
3070 3378 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3071 3379 *
3072 3380 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
@@ -3073,9 +3381,9 @@
3073 3381 *
3074 3382 * @return null|\WP_Error The domain validation error, or `null` if everything's fine.
3075 3383 */
3076 3384 public static function registration_check_domains( $error ) {
3077 - if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3385 + if ( static::is_development_version() ) {
3078 3386 $domains_to_check = array_unique(
3079 3387 array(
3080 3388 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3081 3389 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
@@ -3100,10 +3408,17 @@
3100 3408 * @param mixed $code Error code to log.
3101 3409 * @param mixed $data Data to log.
3102 3410 */
3103 3411 public static function log( $code, $data = null ) {
3412 +
3413 + $raw_log = Jetpack_Options::get_option( 'log', array() );
3414 + // This can be modified by the `jetpack_options` filter, so abort if we don't have an array.
3415 + if ( ! is_array( $raw_log ) ) {
3416 + return;
3417 + }
3418 +
3104 3419 // only grab the latest 200 entries.
3105 - $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3420 + $log = array_slice( $raw_log, -199, 199 );
3106 3421
3107 3422 // Append our event to the log.
3108 3423 $log_entry = array(
3109 3424 'time' => time(),
@@ -3203,8 +3518,15 @@
3203 3518
3204 3519 /**
3205 3520 * Return stat data for WPCOM sync.
3206 3521 *
3522 + * The Sync stats module was this method's last caller and moved to the Connection package's
3523 + * `Heartbeat::generate_stats_array()`, which assembles the heartbeat data through the
3524 + * `jetpack_heartbeat_stats_array` filter. Note the package method does not include the extended
3525 + * data from `get_additional_stat_data()`, so callers relying on `$extended` need to add it themselves.
3526 + *
3527 + * @deprecated 16.2
3528 + *
3207 3529 * @param bool $encode JSON encode the result.
3208 3530 * @param bool $extended Adds additional stats data.
3209 3531 *
3210 3532 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
@@ -3209,10 +3531,15 @@
3209 3531 *
3210 3532 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
3211 3533 */
3212 3534 public static function get_stat_data( $encode = true, $extended = true ) {
3213 - $data = Jetpack_Heartbeat::generate_stats_array();
3535 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3214 3536
3537 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
3538 + ? Heartbeat::get_environment_stats()
3539 + : array();
3540 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), $env_stats );
3541 +
3215 3542 if ( $extended ) {
3216 3543 $additional_data = self::get_additional_stat_data();
3217 3544 $data = array_merge( $data, $additional_data );
3218 3545 }
@@ -3217,9 +3544,9 @@
3217 3544 $data = array_merge( $data, $additional_data );
3218 3545 }
3219 3546
3220 3547 if ( $encode ) {
3221 - return wp_json_encode( $data );
3548 + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
3222 3549 }
3223 3550
3224 3551 return $data;
3225 3552 }
@@ -3291,23 +3618,24 @@
3291 3618 $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
3292 3619 /** Already documented in automattic/jetpack-connection::src/class-client.php */
3293 3620 $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false );
3294 3621
3295 - if ( ! $is_offline_mode ) {
3296 - Jetpack_Connection_Banner::init();
3297 - Jetpack_Connection_Widget::init();
3298 - }
3622 + // Run post-activation actions if needed.
3623 + $this->plugin_post_activation();
3299 3624
3300 3625 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3301 3626 // Upgrade: 1.1 -> 1.1.1
3302 3627 // Check and see if host can verify the Jetpack servers' SSL certificate.
3303 3628 $args = array();
3629 + // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
3304 3630 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3305 3631 }
3306 3632
3307 - Jetpack_Recommendations_Banner::init();
3308 -
3309 - if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3633 + if (
3634 + current_user_can( 'manage_options' )
3635 + && ! self::permit_ssl()
3636 + && ! $is_offline_mode
3637 + ) {
3310 3638 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3311 3639 }
3312 3640
3313 3641 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
@@ -3316,12 +3644,8 @@
3316 3644 if ( ! ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) && ! is_network_admin() ) ) {
3317 3645 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3318 3646 }
3319 3647
3320 - if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3321 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3322 - }
3323 -
3324 3648 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3325 3649
3326 3650 if ( self::is_connection_ready() || $is_offline_mode ) {
3327 3651 // Artificially throw errors in certain specific cases during plugin activation.
@@ -3326,13 +3650,8 @@
3326 3650 if ( self::is_connection_ready() || $is_offline_mode ) {
3327 3651 // Artificially throw errors in certain specific cases during plugin activation.
3328 3652 add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3329 3653 }
3330 -
3331 - // Add custom column in wp-admin/users.php to show whether user is linked.
3332 - add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3333 - add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3334 - add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3335 3654 }
3336 3655
3337 3656 /**
3338 3657 * Adds body classes.
@@ -3365,8 +3684,9 @@
3365 3684 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3366 3685 * This function artificially throws errors for such cases (per a specific list).
3367 3686 *
3368 3687 * @param string $plugin The activated plugin.
3688 + * @throws RuntimeException If a conflicting plugin is detected.
3369 3689 */
3370 3690 public function throw_error_on_activate_plugin( $plugin ) {
3371 3691 $active_modules = self::get_active_modules();
3372 3692
@@ -3379,8 +3699,9 @@
3379 3699 if ( 'stats.php' === basename( $plugin ) ) {
3380 3700 $throw = true;
3381 3701 }
3382 3702 } else {
3703 + // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Checked above. See also https://github.com/phan/phan/issues/1204.
3383 3704 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3384 3705 if ( basename( $plugin ) === basename( $reflection->getFileName() ) ) {
3385 3706 $throw = true;
3386 3707 }
@@ -3387,9 +3708,9 @@
3387 3708 }
3388 3709
3389 3710 if ( $throw ) {
3390 3711 /* translators: Plugin name to deactivate. */
3391 - trigger_error( sprintf( esc_html__( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
3712 + throw new RuntimeException( sprintf( __( 'Jetpack contains the most recent version of the old "%1$s" plugin.', 'jetpack' ), 'WordPress.com Stats' ) );
3392 3713 }
3393 3714 }
3394 3715 }
3395 3716
@@ -3471,8 +3792,9 @@
3471 3792 /**
3472 3793 * Handler for Jetpack remote file uploads.
3473 3794 *
3474 3795 * @access public
3796 + * @return never
3475 3797 */
3476 3798 public function remote_request_handlers() {
3477 3799 switch ( current_filter() ) {
3478 3800 case 'wp_ajax_nopriv_jetpack_upload_file':
@@ -3499,18 +3821,17 @@
3499 3821 if ( ! is_int( $status_code ) ) {
3500 3822 $status_code = 400;
3501 3823 }
3502 3824
3503 - status_header( $status_code );
3504 - die( wp_json_encode( (object) compact( 'error', 'error_description' ) ) );
3825 + wp_send_json( (object) compact( 'error', 'error_description' ), $status_code, JSON_UNESCAPED_SLASHES );
3505 3826 }
3506 3827
3507 - status_header( 200 );
3508 3828 if ( true === $response ) {
3509 - exit;
3829 + status_header( 200 );
3830 + exit( 0 );
3510 3831 }
3511 3832
3512 - die( wp_json_encode( (object) $response ) );
3833 + wp_send_json( (object) $response, 200, JSON_UNESCAPED_SLASHES );
3513 3834 }
3514 3835
3515 3836 /**
3516 3837 * Uploads a file gotten from the global $_FILES.
@@ -3518,9 +3839,9 @@
3518 3839 * the attachment file of the media item (gotten through of the post_id)
3519 3840 * will be updated instead of add a new one.
3520 3841 *
3521 3842 * @param boolean $update_media_item - update media attachment.
3522 - * @return array - An array describing the uploadind files process.
3843 + * @return array|WP_Error - An array describing the uploading files process.
3523 3844 */
3524 3845 public function upload_handler( $update_media_item = false ) {
3525 3846 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
3526 3847 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
@@ -3571,9 +3892,9 @@
3571 3892 return new WP_Error( 'handler_cannot_upload', __( 'The upload handler cannot upload files', 'jetpack' ), 400 );
3572 3893 }
3573 3894
3574 3895 $uploaded_files = array();
3575 - $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3896 + $global_post = $GLOBALS['post'] ?? null;
3576 3897 unset( $GLOBALS['post'] );
3577 3898 if ( empty( $_FILES['media']['name'] ) ) {
3578 3899 // Nothing to process, just return.
3579 3900 return $uploaded_files;
@@ -3580,9 +3901,9 @@
3580 3901 }
3581 3902 foreach ( $_FILES['media']['name'] as $index => $name ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, unslash sniff is wrong. Validation should happen below.
3582 3903 $file = array();
3583 3904 foreach ( $media_keys as $media_key ) {
3584 - $file[ $media_key ] = isset( $_FILES['media'][ $media_key ][ $index ] ) ? $_FILES['media'][ $media_key ][ $index ] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3905 + $file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ] ?? null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,,WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3585 3906 }
3586 3907
3587 3908 list( $hmac_provided, $salt ) = isset( $_POST['_jetpack_file_hmac_media'][ $index ] ) ? explode( ':', filter_var( wp_unslash( $_POST['_jetpack_file_hmac_media'][ $index ] ) ) ) : array( 'no', '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce should have been checked by the caller.
3588 3909
@@ -3628,9 +3949,9 @@
3628 3949 'type' => (string) $edited_media_item->post_mime_type,
3629 3950 'meta' => (array) wp_get_attachment_metadata( $post_id ),
3630 3951 );
3631 3952
3632 - return (array) array( $response );
3953 + return array( $response );
3633 3954 }
3634 3955
3635 3956 $attachment_id = media_handle_upload(
3636 3957 '.jetpack.upload.',
@@ -3669,67 +3990,8 @@
3669 3990 return $uploaded_files;
3670 3991 }
3671 3992
3672 3993 /**
3673 - * Add help to the Jetpack page
3674 - *
3675 - * @since Jetpack (1.2.3)
3676 - * @return void
3677 - */
3678 - public function admin_help() {
3679 - $current_screen = get_current_screen();
3680 -
3681 - // Overview.
3682 - $current_screen->add_help_tab(
3683 - array(
3684 - 'id' => 'home',
3685 - 'title' => __( 'Home', 'jetpack' ),
3686 - 'content' =>
3687 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3688 - '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3689 - '<p>' . __( 'On this page, you are able to view the modules available within Jetpack, learn more about them, and activate or deactivate them as needed.', 'jetpack' ) . '</p>',
3690 - )
3691 - );
3692 -
3693 - // Screen Content.
3694 - if ( current_user_can( 'manage_options' ) ) {
3695 - $current_screen->add_help_tab(
3696 - array(
3697 - 'id' => 'settings',
3698 - 'title' => __( 'Settings', 'jetpack' ),
3699 - 'content' =>
3700 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3701 - '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3702 - '<ol>' .
3703 - '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3704 - '<li>' . __( 'Using the checkboxes next to each module, you can select multiple modules to toggle via the Bulk Actions menu at the top of the list.', 'jetpack' ) . '</li>' .
3705 - '</ol>' .
3706 - '<p>' . __( 'Using the tools on the right, you can search for specific modules, filter by module categories or which are active, or change the sorting order.', 'jetpack' ) . '</p>',
3707 - )
3708 - );
3709 - }
3710 -
3711 - // Help Sidebar.
3712 - $support_url = Redirect::get_url( 'jetpack-support' );
3713 - $faq_url = Redirect::get_url( 'jetpack-faq' );
3714 - $current_screen->set_help_sidebar(
3715 - '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3716 - '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3717 - '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3718 - '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3719 - );
3720 - }
3721 -
3722 - /**
3723 - * Enqueues the jetpack-icons style.
3724 - *
3725 - * @return void
3726 - */
3727 - public function admin_menu_css() {
3728 - wp_enqueue_style( 'jetpack-icons' );
3729 - }
3730 -
3731 - /**
3732 3994 * Add action links for the Jetpack plugin.
3733 3995 *
3734 3996 * @param array $actions Plugin actions.
3735 3997 *
@@ -3735,14 +3997,11 @@
3735 3997 *
3736 3998 * @return array
3737 3999 */
3738 4000 public function plugin_action_links( $actions ) {
3739 - $support_link = ( new Host() )->is_woa_site() ? 'https://wordpress.com/help/contact/' : self::admin_url( 'page=jetpack-debugger' );
3740 -
3741 4001 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
3742 4002 return array_merge(
3743 - array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3744 - array( 'support' => sprintf( '<a href="%s">%s</a>', $support_link, __( 'Support', 'jetpack' ) ) ),
4003 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack-settings#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3745 4004 $actions
3746 4005 );
3747 4006 }
3748 4007
@@ -3749,53 +4008,8 @@
3749 4008 return $actions;
3750 4009 }
3751 4010
3752 4011 /**
3753 - * Add an activation modal to the plugins page and the main dashboard.
3754 - *
3755 - * @param string $hook The current admin page.
3756 - *
3757 - * @return void
3758 - */
3759 - public function activate_dialog( $hook ) {
3760 - /*
3761 - * We do not need it on other plugin pages,
3762 - * or when Jetpack is already connected.
3763 - */
3764 - if (
3765 - ! in_array( $hook, array( 'plugins.php', 'index.php' ), true )
3766 - || self::is_connection_ready()
3767 - ) {
3768 - return;
3769 - }
3770 -
3771 - // add an activation script that will pick up deactivation actions for the Jetpack plugin.
3772 - Assets::register_script(
3773 - 'jetpack-full-activation-modal-js',
3774 - '_inc/build/activation-modal.js',
3775 - JETPACK__PLUGIN_FILE,
3776 - array(
3777 - 'enqueue' => true,
3778 - 'in_footer' => true,
3779 - 'textdomain' => 'jetpack',
3780 - 'dependencies' => array(
3781 - 'wp-polyfill',
3782 - 'wp-components',
3783 - ),
3784 - )
3785 - );
3786 -
3787 - // Add objects to be passed to the initial state of the app.
3788 - // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3789 - wp_add_inline_script( 'jetpack-full-activation-modal-js', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state() ) ) . '"));', 'before' );
3790 -
3791 - // Adds Connection package initial state.
3792 - wp_add_inline_script( 'jetpack-full-activation-modal-js', Connection_Initial_State::render(), 'before' );
3793 -
3794 - add_action( 'admin_notices', array( $this, 'jetpack_plugin_portal_containers' ) );
3795 - }
3796 -
3797 - /**
3798 4012 * Adds the deactivation warning modal for Jetpack.
3799 4013 *
3800 4014 * @param string $hook The current admin page.
3801 4015 *
@@ -3815,14 +4029,10 @@
3815 4029 'jetpack-plugins-page-js',
3816 4030 '_inc/build/plugins-page.js',
3817 4031 JETPACK__PLUGIN_FILE,
3818 4032 array(
3819 - 'in_footer' => true,
3820 - 'textdomain' => 'jetpack',
3821 - 'dependencies' => array(
3822 - 'wp-polyfill',
3823 - 'wp-components',
3824 - ),
4033 + 'in_footer' => true,
4034 + 'textdomain' => 'jetpack',
3825 4035 )
3826 4036 );
3827 4037 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3828 4038
@@ -3827,9 +4037,9 @@
3827 4037 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3828 4038
3829 4039 // Add objects to be passed to the initial state of the app.
3830 4040 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3831 - wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state() ) ) . '"));', 'before' );
4041 + wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_plugins_page_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
3832 4042
3833 4043 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
3834 4044 }
3835 4045 }
@@ -3878,9 +4088,9 @@
3878 4088 // @todo provide way to go to specific calypso env.
3879 4089 self::get_calypso_host() . 'jetpack/connect'
3880 4090 )
3881 4091 );
3882 - exit;
4092 + exit( 0 );
3883 4093 }
3884 4094 }
3885 4095
3886 4096 /*
@@ -3915,8 +4125,28 @@
3915 4125 * Done!
3916 4126 */
3917 4127
3918 4128 /**
4129 + * Build the user-facing description stored alongside a registration error code.
4130 + *
4131 + * @since 16.2
4132 + *
4133 + * @param string $error_code The WP_Error code.
4134 + * @param string $message The WP_Error message.
4135 + * @return string The description, empty when the message is not user-facing copy.
4136 + */
4137 + public static function get_registration_error_description( $error_code, $message ) {
4138 + // Manager::validate_remote_register_response() does not always put user-facing copy in the
4139 + // message slot: wpcom_5??, wpcom_408 and wpcom_bad_response store the HTTP status there,
4140 + // and jetpack_id stores the raw response body, which can also overflow the state cookie.
4141 + if ( 'jetpack_id' === $error_code || is_numeric( $message ) ) {
4142 + return '';
4143 + }
4144 +
4145 + return mb_substr( (string) $message, 0, 250 );
4146 + }
4147 +
4148 + /**
3919 4149 * Handles the page load events for the Jetpack admin page
3920 4150 */
3921 4151 public function admin_page_load() {
3922 4152 $error = false;
@@ -3952,10 +4182,11 @@
3952 4182 $registered = static::connection()->try_registration();
3953 4183 if ( is_wp_error( $registered ) ) {
3954 4184 $error = $registered->get_error_code();
3955 4185 self::state( 'error', $error );
3956 - self::state( 'error', $registered->get_error_message() );
3957 4186
4187 + self::state( 'error_description', self::get_registration_error_description( $error, $registered->get_error_message() ) );
4188 +
3958 4189 /**
3959 4190 * Jetpack registration Error.
3960 4191 *
3961 4192 * @since 7.5.0
@@ -3979,12 +4210,8 @@
3979 4210 do_action( 'jetpack_connection_register_success', $from );
3980 4211
3981 4212 $url = $this->build_connect_url( true, $redirect, $from );
3982 4213
3983 - if ( ! empty( $_GET['onboarding'] ) ) {
3984 - $url = add_query_arg( 'onboarding', rawurlencode_deep( wp_unslash( $_GET['onboarding'] ) ), $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3985 - }
3986 -
3987 4214 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
3988 4215 $url = add_query_arg( 'auth_approved', 'true', $url );
3989 4216 }
3990 4217
@@ -3989,9 +4216,9 @@
3989 4216 }
3990 4217
3991 4218 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3992 4219 wp_safe_redirect( $url );
3993 - exit;
4220 + exit( 0 );
3994 4221 case 'activate':
3995 4222 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3996 4223 $error = 'cheatin';
3997 4224 break;
@@ -4005,9 +4232,9 @@
4005 4232 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4006 4233 }
4007 4234 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4008 4235 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4009 - exit;
4236 + exit( 0 );
4010 4237 case 'activate_default_modules':
4011 4238 check_admin_referer( 'activate_default_modules' );
4012 4239 self::log( 'activate_default_modules' );
4013 4240 self::restate();
@@ -4015,9 +4242,9 @@
4015 4242 $max_version = isset( $_GET['max_version'] ) ? sanitize_text_field( wp_unslash( $_GET['max_version'] ) ) : false;
4016 4243 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_GET['other_modules'] ) ) : array();
4017 4244 self::activate_default_modules( $min_version, $max_version, $other_modules );
4018 4245 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4019 - exit;
4246 + exit( 0 );
4020 4247 case 'disconnect':
4021 4248 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4022 4249 $error = 'cheatin';
4023 4250 break;
@@ -4026,9 +4253,9 @@
4026 4253 check_admin_referer( 'jetpack-disconnect' );
4027 4254 self::log( 'disconnect' );
4028 4255 self::disconnect();
4029 4256 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4030 - exit;
4257 + exit( 0 );
4031 4258 case 'reconnect':
4032 4259 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4033 4260 $error = 'cheatin';
4034 4261 break;
@@ -4039,9 +4266,9 @@
4039 4266 self::disconnect();
4040 4267
4041 4268 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4042 4269 wp_safe_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4043 - exit;
4270 + exit( 0 );
4044 4271 case 'deactivate':
4045 4272 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4046 4273 $error = 'cheatin';
4047 4274 break;
@@ -4055,9 +4282,9 @@
4055 4282 self::state( 'message', 'module_deactivated' );
4056 4283 }
4057 4284 self::state( 'module', $modules );
4058 4285 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4059 - exit;
4286 + exit( 0 );
4060 4287 case 'unlink':
4061 4288 $redirect = isset( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : '';
4062 4289 check_admin_referer( 'jetpack-unlink' );
4063 4290 self::log( 'unlink' );
@@ -4067,32 +4294,9 @@
4067 4294 wp_safe_redirect( admin_url() );
4068 4295 } else {
4069 4296 wp_safe_redirect( self::admin_url( array( 'page' => rawurlencode( $redirect ) ) ) );
4070 4297 }
4071 - exit;
4072 - case 'onboard':
4073 - if ( ! current_user_can( 'manage_options' ) ) {
4074 - wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4075 - } else {
4076 - self::create_onboarding_token();
4077 - $url = $this->build_connect_url( true );
4078 -
4079 - $token = Jetpack_Options::get_option( 'onboarding' );
4080 -
4081 - if ( false !== ( $token ) ) {
4082 - $url = add_query_arg( 'onboarding', $token, $url );
4083 - }
4084 -
4085 - $calypso_env = $this->get_calypso_env();
4086 - if ( ! empty( $calypso_env ) ) {
4087 - $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4088 - }
4089 -
4090 - add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4091 - wp_safe_redirect( $url );
4092 - exit;
4093 - }
4094 - exit;
4298 + exit( 0 );
4095 4299 default:
4096 4300 /**
4097 4301 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4098 4302 *
@@ -4108,9 +4312,10 @@
4108 4312 if ( ! $error ) {
4109 4313 self::activate_new_modules( true );
4110 4314 }
4111 4315
4112 - $message_code = self::state( 'message' );
4316 + $activated_manage = false;
4317 + $message_code = self::state( 'message' );
4113 4318 if ( self::state( 'optin-manage' ) ) {
4114 4319 $activated_manage = $message_code;
4115 4320 $message_code = 'jetpack-manage';
4116 4321 }
@@ -4249,9 +4454,9 @@
4249 4454 $module_slugs[] = $module_slug;
4250 4455 $module_names[] = "<strong>{$module['name']}</strong>";
4251 4456 }
4252 4457
4253 - $module_slugs = join( ',', $module_slugs );
4458 + $module_slugs = implode( ',', $module_slugs );
4254 4459 ?>
4255 4460 <div id="message" class="jetpack-message jetpack-err">
4256 4461 <div class="squeezer">
4257 4462 <h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
@@ -4284,17 +4489,19 @@
4284 4489 count( $privacy_checks ),
4285 4490 '%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4286 4491 'jetpack'
4287 4492 ),
4288 - wp_nonce_url(
4289 - self::admin_url(
4290 - array(
4291 - 'page' => 'jetpack',
4292 - 'action' => 'deactivate',
4293 - 'module' => rawurlencode( $module_slugs ),
4294 - )
4295 - ),
4296 - "jetpack_deactivate-$module_slugs"
4493 + esc_url(
4494 + wp_nonce_url(
4495 + self::admin_url(
4496 + array(
4497 + 'page' => 'jetpack',
4498 + 'action' => 'deactivate',
4499 + 'module' => rawurlencode( $module_slugs ),
4500 + )
4501 + ),
4502 + "jetpack_deactivate-$module_slugs"
4503 + )
4297 4504 ),
4298 4505 esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4299 4506 ),
4300 4507 array(
@@ -4312,37 +4519,8 @@
4312 4519 endif;
4313 4520 }
4314 4521
4315 4522 /**
4316 - * We can't always respond to a signed XML-RPC request with a
4317 - * helpful error message. In some circumstances, doing so could
4318 - * leak information.
4319 - *
4320 - * Instead, track that the error occurred via a Jetpack_Option,
4321 - * and send that data back in the heartbeat.
4322 - * All this does is increment a number, but it's enough to find
4323 - * trends.
4324 - *
4325 - * @param WP_Error $xmlrpc_error The error produced during
4326 - * signature validation.
4327 - */
4328 - public function track_xmlrpc_error( $xmlrpc_error ) {
4329 - $code = is_wp_error( $xmlrpc_error )
4330 - ? $xmlrpc_error->get_error_code()
4331 - : 'should-not-happen';
4332 -
4333 - $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4334 - if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4335 - // No need to update the option if we already have
4336 - // this code stored.
4337 - return;
4338 - }
4339 - $xmlrpc_errors[ $code ] = true;
4340 -
4341 - Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4342 - }
4343 -
4344 - /**
4345 4523 * Initialize the jetpack stats instance only when needed
4346 4524 *
4347 4525 * @return void
4348 4526 */
@@ -4440,9 +4618,9 @@
4440 4618 if ( is_network_admin() ) {
4441 4619 $url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4442 4620 }
4443 4621
4444 - $calypso_env = self::get_calypso_env();
4622 + $calypso_env = ( new Host() )->get_calypso_env();
4445 4623
4446 4624 if ( ! empty( $calypso_env ) ) {
4447 4625 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4448 4626 }
@@ -4465,9 +4643,9 @@
4465 4643 return $this->build_connect_url( $raw, $redirect, $from, true );
4466 4644 }
4467 4645 }
4468 4646
4469 - $url = $this->build_authorize_url( $redirect );
4647 + $url = ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4470 4648 }
4471 4649
4472 4650 if ( $from ) {
4473 4651 $url = add_query_arg( 'from', $from, $url );
@@ -4492,66 +4670,30 @@
4492 4670 * @param null $deprecated Deprecated since Jetpack 10.9.
4493 4671 *
4494 4672 * @todo Update default value for redirect since the called function expects a string.
4495 4673 *
4674 + * @deprecated 13.4
4675 + *
4496 4676 * @return mixed|void
4497 4677 */
4498 4678 public static function build_authorize_url( $redirect = false, $deprecated = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
4679 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::build_authorize_url' );
4499 4680
4500 - add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4501 - add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4502 -
4503 - $c8n = self::connection();
4504 - $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4505 -
4506 - remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4507 - remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4508 -
4509 - /**
4510 - * Filter the URL used when authorizing a user to a WordPress.com account.
4511 - *
4512 - * @since 8.9.0
4513 - *
4514 - * @param string $url Connection URL.
4515 - */
4516 - return apply_filters( 'jetpack_build_authorize_url', $url );
4681 + return ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4517 4682 }
4518 4683
4519 4684 /**
4520 4685 * Filters the connection URL parameter array.
4521 4686 *
4687 + * @deprecated 13.4
4688 + *
4522 4689 * @param array $args default URL parameters used by the package.
4523 4690 * @return array the modified URL arguments array.
4524 4691 */
4525 4692 public static function filter_connect_request_body( $args ) {
4526 - if (
4527 - Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4528 - && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4529 - ) {
4530 - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4531 - $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4532 - ? $gp_locale->slug
4533 - : '';
4534 - }
4693 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_request_body' );
4535 4694
4536 - $tracking = new Tracking();
4537 - $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4538 -
4539 - $args = array_merge(
4540 - $args,
4541 - array(
4542 - '_ui' => $tracks_identity['_ui'],
4543 - '_ut' => $tracks_identity['_ut'],
4544 - )
4545 - );
4546 -
4547 - $calypso_env = self::get_calypso_env();
4548 -
4549 - if ( ! empty( $calypso_env ) ) {
4550 - $args['calypso_env'] = $calypso_env;
4551 - }
4552 -
4553 - return $args;
4695 + return Authorize_Redirect::filter_connect_request_body( $args );
4554 4696 }
4555 4697
4556 4698 /**
4557 4699 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
@@ -4588,41 +4730,19 @@
4588 4730 return $current_user_connection_data;
4589 4731 }
4590 4732
4591 4733 /**
4592 - * Filters the URL that will process the connection data. It can be different from the URL
4593 - * that we send the user to after everything is done.
4594 - *
4595 - * @param String $processing_url the default redirect URL used by the package.
4596 - * @return String the modified URL.
4597 - *
4598 - * @deprecated since Jetpack 9.5.0
4599 - */
4600 - public static function filter_connect_processing_url( $processing_url ) {
4601 - _deprecated_function( __METHOD__, 'jetpack-9.5' );
4602 -
4603 - $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4604 - return $processing_url;
4605 - }
4606 -
4607 - /**
4608 4734 * Filters the redirection URL that is used for connect requests. The redirect
4609 4735 * URL should return the user back to the Jetpack console.
4610 4736 *
4737 + * @deprecated 13.4
4738 + *
4611 4739 * @param String $redirect the default redirect URL used by the package.
4612 4740 * @return String the modified URL.
4613 4741 */
4614 4742 public static function filter_connect_redirect_url( $redirect ) {
4615 - $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4616 - $redirect = $redirect
4617 - ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4618 - : $jetpack_admin_page;
4619 -
4620 - if ( isset( $_REQUEST['is_multisite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- not making a site change here.
4621 - $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4622 - }
4623 -
4624 - return $redirect;
4743 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_redirect_url' );
4744 + return Authorize_Redirect::filter_connect_redirect_url( $redirect );
4625 4745 }
4626 4746
4627 4747 /**
4628 4748 * This action fires at the beginning of the Manager::authorize method.
@@ -4686,11 +4806,8 @@
4686 4806 *
4687 4807 * @param array $data The request data.
4688 4808 */
4689 4809 public static function authorize_ending_authorized( $data ) {
4690 - // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4691 - self::invalidate_onboarding_token();
4692 -
4693 4810 // If redirect_uri is SSO, ensure SSO module is enabled.
4694 4811 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4695 4812
4696 4813 /** This filter is documented in class.jetpack-cli.php */
@@ -4814,13 +4931,9 @@
4814 4931 *
4815 4932 * @return int 0 if the same sort or (+/-) to indicate which is greater.
4816 4933 */
4817 4934 public static function sort_modules( $a, $b ) {
4818 - if ( $a['sort'] === $b['sort'] ) {
4819 - return 0;
4820 - }
4821 -
4822 - return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4935 + return $a['sort'] <=> $b['sort'];
4823 4936 }
4824 4937
4825 4938 /**
4826 4939 * Recheck SSL status for use via an AJAX call.
@@ -4834,10 +4947,12 @@
4834 4947 $result = self::permit_ssl( true );
4835 4948 wp_send_json(
4836 4949 array(
4837 4950 'enabled' => $result,
4838 - 'message' => get_transient( 'jetpack_https_test_message' ),
4839 - )
4951 + 'message' => self::get_ssl_test_message(),
4952 + ),
4953 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
4954 + JSON_UNESCAPED_SLASHES
4840 4955 );
4841 4956 }
4842 4957
4843 4958 /* Client API */
@@ -4844,8 +4959,10 @@
4844 4959
4845 4960 /**
4846 4961 * Verify the onboarding token.
4847 4962 *
4963 + * @deprecated since 13.9
4964 + *
4848 4965 * @param array $token_data Token data.
4849 4966 * @param string $token Token value.
4850 4967 * @param string $request_data JSON-encoded request data.
4851 4968 *
@@ -4851,8 +4968,9 @@
4851 4968 *
4852 4969 * @return mixed
4853 4970 */
4854 4971 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
4972 + _deprecated_function( __METHOD__, '13.9' );
4855 4973 // Default to a blog token.
4856 4974 $token_type = 'blog';
4857 4975
4858 4976 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
@@ -4880,9 +4998,9 @@
4880 4998 $jp_user = get_user_by( 'email', $jpo_user );
4881 4999 if ( is_a( $jp_user, 'WP_User' ) ) {
4882 5000 wp_set_current_user( $jp_user->ID );
4883 5001 $user_can = is_multisite()
4884 - ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5002 + ? current_user_can_for_site( get_current_blog_id(), 'manage_options' )
4885 5003 : current_user_can( 'manage_options' );
4886 5004 if ( $user_can ) {
4887 5005 $token_type = 'user';
4888 5006 $token->external_user_id = $jp_user->ID;
@@ -4899,11 +5017,13 @@
4899 5017
4900 5018 /**
4901 5019 * Create a random secret for validating onboarding payload
4902 5020 *
5021 + * @deprecated since 13.9
4903 5022 * @return string Secret token
4904 5023 */
4905 5024 public static function create_onboarding_token() {
5025 + _deprecated_function( __METHOD__, '13.9' );
4906 5026 $token = Jetpack_Options::get_option( 'onboarding' );
4907 5027 if ( false === ( $token ) ) {
4908 5028 $token = wp_generate_password( 32, false );
4909 5029 Jetpack_Options::update_option( 'onboarding', $token );
@@ -4914,11 +5034,13 @@
4914 5034
4915 5035 /**
4916 5036 * Remove the onboarding token
4917 5037 *
5038 + * @deprecated since 13.9
4918 5039 * @return bool True on success, false on failure
4919 5040 */
4920 5041 public static function invalidate_onboarding_token() {
5042 + _deprecated_function( __METHOD__, '13.9' );
4921 5043 return Jetpack_Options::delete_option( 'onboarding' );
4922 5044 }
4923 5045
4924 5046 /**
@@ -4923,8 +5045,10 @@
4923 5045
4924 5046 /**
4925 5047 * Validate an onboarding token for a specific action
4926 5048 *
5049 + * @deprecated since 13.9
5050 + *
4927 5051 * @param string $token Onboarding token.
4928 5052 * @param string $action Action name.
4929 5053 *
4930 5054 * @return boolean True if token/action pair is accepted, false if not
@@ -4929,8 +5053,9 @@
4929 5053 *
4930 5054 * @return boolean True if token/action pair is accepted, false if not
4931 5055 */
4932 5056 public static function validate_onboarding_token_action( $token, $action ) {
5057 + _deprecated_function( __METHOD__, '13.9' );
4933 5058 // Compare tokens, bail if tokens do not match.
4934 5059 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4935 5060 return false;
4936 5061 }
@@ -4956,39 +5081,41 @@
4956 5081 * @return boolean
4957 5082 * @since 2.3.3
4958 5083 */
4959 5084 public static function permit_ssl( $force_recheck = false ) {
4960 - // Do some fancy tests to see if ssl is being supported.
4961 - if ( ! $force_recheck ) {
4962 - $ssl = get_transient( 'jetpack_https_test' );
5085 + if ( ! method_exists( Heartbeat::class, 'permit_ssl' ) ) {
5086 + // Skip the SSL-fail notice when the check cannot run.
5087 + return true;
4963 5088 }
4964 5089
4965 - if ( $force_recheck || false === $ssl ) {
4966 - $message = '';
4967 - if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4968 - $ssl = 0;
4969 - } else {
4970 - $ssl = 1;
5090 + return Heartbeat::permit_ssl( $force_recheck );
5091 + }
4971 5092
4972 - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4973 - $ssl = 0;
4974 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4975 - } else {
4976 - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4977 - if ( is_wp_error( $response ) ) {
4978 - $ssl = 0;
4979 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4980 - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4981 - $ssl = 0;
4982 - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4983 - }
4984 - }
4985 - }
4986 - set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4987 - set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5093 + /**
5094 + * Returns a localized message describing the last SSL connectivity failure, if any.
5095 + *
5096 + * The Connection package's canonical SSL check stores a neutral reason code; this maps it to
5097 + * a translated, `jetpack`-domain message for display in the admin notice and AJAX recheck.
5098 + *
5099 + * @since 16.1
5100 + *
5101 + * @return string The localized message, or an empty string when there is no failure.
5102 + */
5103 + public static function get_ssl_test_message() {
5104 + if ( ! method_exists( Heartbeat::class, 'get_ssl_test_error' ) ) {
5105 + return '';
4988 5106 }
4989 5107
4990 - return (bool) $ssl;
5108 + $error = Heartbeat::get_ssl_test_error();
5109 +
5110 + switch ( $error['code'] ) {
5111 + case 'no_ssl_support':
5112 + return __( 'WordPress reports no SSL support', 'jetpack' );
5113 + case 'bad_response':
5114 + return __( 'Response was not OK: ', 'jetpack' ) . $error['detail'];
5115 + default:
5116 + return '';
5117 + }
4991 5118 }
4992 5119
4993 5120 /**
4994 5121 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
@@ -5007,9 +5134,9 @@
5007 5134 <p><?php esc_html_e( 'Your site could not connect to WordPress.com via HTTPS. This could be due to any number of reasons, including faulty SSL certificates, misconfigured or missing SSL libraries, or network issues.', 'jetpack' ); ?></p>
5008 5135 <p>
5009 5136 <?php esc_html_e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5010 5137 <a href="#" id="jetpack-recheck-ssl-button"><?php esc_html_e( 'Try again', 'jetpack' ); ?></a>
5011 - <span id="jetpack-recheck-ssl-output"><?php echo esc_html( get_transient( 'jetpack_https_test_message' ) ); ?></span>
5138 + <span id="jetpack-recheck-ssl-output"><?php echo esc_html( self::get_ssl_test_message() ); ?></span>
5012 5139 </p>
5013 5140 <p>
5014 5141 <?php
5015 5142 printf(
@@ -5028,18 +5155,18 @@
5028 5155 <script type="text/javascript">
5029 5156 jQuery( document ).ready( function( $ ) {
5030 5157 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5031 5158 var $this = $( this );
5032 - $this.html( <?php echo wp_json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5159 + $this.html( <?php echo wp_json_encode( esc_html__( 'Checking', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5033 5160 $( '#jetpack-recheck-ssl-output' ).html( '' );
5034 5161 e.preventDefault();
5035 - var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce ); ?> };
5162 + var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> };
5036 5163 $.post( ajaxurl, data )
5037 5164 .done( function( response ) {
5038 5165 if ( response.enabled ) {
5039 5166 $( '#jetpack-ssl-warning' ).hide();
5040 5167 } else {
5041 - this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5168 + this.html( <?php echo wp_json_encode( esc_html__( 'Try again', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5042 5169 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5043 5170 }
5044 5171 }.bind( $this ) );
5045 5172 } );
@@ -5065,26 +5192,8 @@
5065 5192 return $jetpack->connection_manager;
5066 5193 }
5067 5194
5068 5195 /**
5069 - * Creates two secret tokens and the end of life timestamp for them.
5070 - *
5071 - * Note these tokens are unique per call, NOT static per site for connecting.
5072 - *
5073 - * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5074 - *
5075 - * @since 2.6
5076 - * @param String $action The action name.
5077 - * @param Integer $user_id The user identifier.
5078 - * @param Integer $exp Expiration time in seconds.
5079 - * @return array
5080 - */
5081 - public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5082 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5083 - return self::connection()->generate_secrets( $action, $user_id, $exp );
5084 - }
5085 -
5086 - /**
5087 5196 * Get verification secrets.
5088 5197 *
5089 5198 * @param string $action Action name.
5090 5199 * @param int $user_id User ID.
@@ -5105,35 +5214,8 @@
5105 5214 return $secrets;
5106 5215 }
5107 5216
5108 5217 /**
5109 - * Register a connection.
5110 - *
5111 - * @deprecated Jetpack 9.7.0
5112 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
5113 - *
5114 - * @return bool|WP_Error
5115 - */
5116 - public static function register() {
5117 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5118 - return static::connection()->try_registration( false );
5119 - }
5120 -
5121 - /**
5122 - * Filters the registration request body to include tracking properties.
5123 - *
5124 - * @deprecated Jetpack 9.7.0
5125 - * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5126 - *
5127 - * @param array $properties Token request properties.
5128 - * @return array amended properties.
5129 - */
5130 - public static function filter_register_request_body( $properties ) {
5131 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5132 - return Connection_Utils::filter_register_request_body( $properties );
5133 - }
5134 -
5135 - /**
5136 5218 * Filters the token request body to include tracking properties.
5137 5219 *
5138 5220 * @param array $properties Token request properties.
5139 5221 *
@@ -5154,9 +5236,9 @@
5154 5236
5155 5237 /**
5156 5238 * If the db version is showing something other that what we've got now, bump it to current.
5157 5239 *
5158 - * @return bool: True if the option was incorrect and updated, false if nothing happened.
5240 + * @return bool True if the option was incorrect and updated, false if nothing happened.
5159 5241 */
5160 5242 public static function maybe_set_version_option() {
5161 5243 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5162 5244 if ( JETPACK__VERSION !== $version ) {
@@ -5174,21 +5256,8 @@
5174 5256
5175 5257 /* Client Server API */
5176 5258
5177 5259 /**
5178 - * Loads the Jetpack XML-RPC client.
5179 - * No longer necessary, as the XML-RPC client will be automagically loaded.
5180 - *
5181 - * Note: we cannot remove this function yet as it is used in this plugin:
5182 - * https://wordpress.org/plugins/jetpack-subscription-form/
5183 - *
5184 - * @deprecated since 7.7.0
5185 - */
5186 - public static function load_xml_rpc_client() {
5187 - _deprecated_function( __METHOD__, 'jetpack-7.7' );
5188 - }
5189 -
5190 - /**
5191 5260 * State is passed via cookies from one request to the next, but never to subsequent requests.
5192 5261 * SET: state( $key, $value );
5193 5262 * GET: $value = state( $key );
5194 5263 *
@@ -5262,20 +5331,8 @@
5262 5331
5263 5332 self::state( 'privacy_checks', $privacy_checks );
5264 5333 }
5265 5334
5266 - /**
5267 - * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5268 - *
5269 - * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5270 - *
5271 - * @param string $url WordPress.com static resource URL.
5272 - */
5273 - public static function staticize_subdomain( $url ) {
5274 - _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5275 - return Assets::staticize_subdomain( $url );
5276 - }
5277 -
5278 5335 /* JSON API Authorization */
5279 5336
5280 5337 /**
5281 5338 * Handles the login action for Authorizing the JSON API
@@ -5280,13 +5337,14 @@
5280 5337 /**
5281 5338 * Handles the login action for Authorizing the JSON API
5282 5339 */
5283 5340 public function login_form_json_api_authorization() {
5284 - $this->verify_json_api_authorization_request();
5341 + $authorize_json_api = new Authorize_Json_Api();
5342 + $authorize_json_api->verify_json_api_authorization_request();
5285 5343
5286 - add_action( 'wp_login', array( $this, 'store_json_api_authorization_token' ), 10, 2 );
5344 + add_action( 'wp_login', array( $authorize_json_api, 'store_json_api_authorization_token' ), 10, 2 );
5287 5345
5288 - add_action( 'login_message', array( $this, 'login_message_json_api_authorization' ) );
5346 + add_action( 'login_message', array( $authorize_json_api, 'login_message_json_api_authorization' ) );
5289 5347 add_action( 'login_form', array( $this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5290 5348 add_filter( 'site_url', array( $this, 'post_login_form_to_signed_url' ), 10, 3 );
5291 5349 }
5292 5350
@@ -5324,16 +5382,17 @@
5324 5382
5325 5383 /**
5326 5384 * If someone logs in to approve API access, store the Access Code in usermeta.
5327 5385 *
5386 + * @deprecated 13.4
5387 + *
5328 5388 * @param string $user_login Unused.
5329 5389 * @param WP_User $user User logged in.
5330 5390 */
5331 5391 public function store_json_api_authorization_token( $user_login, $user ) {
5332 - add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5333 - add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_public_api_domain' ) );
5334 - $token = wp_generate_password( 32, false );
5335 - update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5392 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::store_json_api_authorization_token' );
5393 +
5394 + return ( new Authorize_Json_Api() )->store_json_api_authorization_token( $user_login, $user );
5336 5395 }
5337 5396
5338 5397 /**
5339 5398 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
@@ -5339,23 +5398,29 @@
5339 5398 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5340 5399 *
5341 5400 * To be used with a filter of allowed domains for a redirect.
5342 5401 *
5402 + * @deprecated 13.4
5403 + *
5343 5404 * @param array $domains Allowed WP.com Environments.
5344 5405 */
5345 5406 public function allow_wpcom_public_api_domain( $domains ) {
5346 - $domains[] = 'public-api.wordpress.com';
5347 - return $domains;
5407 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Status\\Host::allow_wpcom_public_api_domain' );
5408 +
5409 + return Host::allow_wpcom_public_api_domain( $domains );
5348 5410 }
5349 5411
5350 5412 /**
5351 5413 * Check if the redirect is encoded.
5352 5414 *
5415 + * @deprecated 13.4
5416 + *
5353 5417 * @param string $redirect_url Redirect URL.
5354 5418 *
5355 5419 * @return bool If redirect has been encoded.
5356 5420 */
5357 5421 public static function is_redirect_encoded( $redirect_url ) {
5422 + _deprecated_function( __METHOD__, 'jetpack-13.4' );
5358 5423 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5359 5424 }
5360 5425
5361 5426 /**
@@ -5373,8 +5438,10 @@
5373 5438
5374 5439 /**
5375 5440 * Add the Access Code details to the public-api.wordpress.com redirect.
5376 5441 *
5442 + * @deprecated 13.4
5443 + *
5377 5444 * @param string $redirect_to URL.
5378 5445 * @param string $original_redirect_to URL.
5379 5446 * @param WP_User $user WP_User for the redirect.
5380 5447 *
@@ -5380,23 +5447,18 @@
5380 5447 *
5381 5448 * @return string
5382 5449 */
5383 5450 public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5384 - return add_query_arg(
5385 - urlencode_deep(
5386 - array(
5387 - 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5388 - 'jetpack-user-id' => (int) $user->ID,
5389 - 'jetpack-state' => $this->json_api_authorization_request['state'],
5390 - )
5391 - ),
5392 - $redirect_to
5393 - );
5451 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::add_token_to_login_redirect_json_api_authorization' );
5452 +
5453 + return ( new Authorize_Json_Api() )->add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user );
5394 5454 }
5395 5455
5396 5456 /**
5397 5457 * Verifies the request by checking the signature
5398 5458 *
5459 + * @deprecated 13.4
5460 + *
5399 5461 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5400 5462 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5401 5463 *
5402 5464 * @param null|array $environment Value to override $_REQUEST.
@@ -5401,194 +5463,24 @@
5401 5463 *
5402 5464 * @param null|array $environment Value to override $_REQUEST.
5403 5465 */
5404 5466 public function verify_json_api_authorization_request( $environment = null ) {
5405 - $environment = $environment === null
5406 - ? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function.
5407 - : $environment;
5467 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::verify_json_api_authorization_request' );
5408 5468
5409 - list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
5410 - $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
5411 - if ( ! $token || empty( $token->secret ) ) {
5412 - wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5413 - }
5414 -
5415 - $die_error = __( 'Someone may be trying to trick you into giving them access to your site. Or it could be you just encountered a bug :). Either way, please close this window.', 'jetpack' );
5416 -
5417 - // Host has encoded the request URL, probably as a result of a bad http => https redirect.
5418 - if ( self::is_redirect_encoded( esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no site changes, we're erroring out.
5419 - /**
5420 - * Jetpack authorisation request Error.
5421 - *
5422 - * @since 7.5.0
5423 - */
5424 - do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5425 - $die_error = sprintf(
5426 - /* translators: %s is a URL */
5427 - __( 'Your site is incorrectly double-encoding redirects from http to https. This is preventing Jetpack from authenticating your connection. Please visit our <a href="%s">support page</a> for details about how to resolve this.', 'jetpack' ),
5428 - Redirect::get_url( 'jetpack-support-double-encoding' )
5429 - );
5430 - }
5431 -
5432 - $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5433 -
5434 - if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5435 - $signature = $jetpack_signature->sign_request(
5436 - $environment['token'],
5437 - $environment['timestamp'],
5438 - $environment['nonce'],
5439 - '',
5440 - 'GET',
5441 - $environment['jetpack_json_api_original_query'],
5442 - null,
5443 - true
5444 - );
5445 - } else {
5446 - $signature = $jetpack_signature->sign_current_request(
5447 - array(
5448 - 'body' => null,
5449 - 'method' => 'GET',
5450 - )
5451 - );
5452 - }
5453 -
5454 - if ( ! $signature ) {
5455 - wp_die(
5456 - wp_kses(
5457 - $die_error,
5458 - array(
5459 - 'a' => array(
5460 - 'href' => array(),
5461 - ),
5462 - )
5463 - )
5464 - );
5465 - } elseif ( is_wp_error( $signature ) ) {
5466 - wp_die(
5467 - wp_kses(
5468 - $die_error,
5469 - array(
5470 - 'a' => array(
5471 - 'href' => array(),
5472 - ),
5473 - )
5474 - )
5475 - );
5476 - } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5477 - if ( is_ssl() ) {
5478 - // If we signed an HTTP request on the Jetpack Servers, but got redirected to HTTPS by the local blog, check the HTTP signature as well.
5479 - $signature = $jetpack_signature->sign_current_request(
5480 - array(
5481 - 'scheme' => 'http',
5482 - 'body' => null,
5483 - 'method' => 'GET',
5484 - )
5485 - );
5486 - if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5487 - wp_die(
5488 - wp_kses(
5489 - $die_error,
5490 - array(
5491 - 'a' => array(
5492 - 'href' => array(),
5493 - ),
5494 - )
5495 - )
5496 - );
5497 - }
5498 - } else {
5499 - wp_die(
5500 - wp_kses(
5501 - $die_error,
5502 - array(
5503 - 'a' => array(
5504 - 'href' => array(),
5505 - ),
5506 - )
5507 - )
5508 - );
5509 - }
5510 - }
5511 -
5512 - $timestamp = (int) $environment['timestamp'];
5513 - $nonce = stripslashes( (string) $environment['nonce'] );
5514 -
5515 - if ( ! $this->connection_manager ) {
5516 - $this->connection_manager = new Connection_Manager();
5517 - }
5518 -
5519 - if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
5520 - // De-nonce the nonce, at least for 5 minutes.
5521 - // We have to reuse this nonce at least once (used the first time when the initial request is made, used a second time when the login form is POSTed).
5522 - $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5523 - if ( $old_nonce_time < time() - 300 ) {
5524 - wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
5525 - }
5526 - }
5527 -
5528 - $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
5529 - $data_filters = array(
5530 - 'state' => 'opaque',
5531 - 'client_id' => 'int',
5532 - 'client_title' => 'string',
5533 - 'client_image' => 'url',
5534 - );
5535 -
5536 - foreach ( $data_filters as $key => $sanitation ) {
5537 - if ( ! isset( $data->$key ) ) {
5538 - wp_die(
5539 - wp_kses(
5540 - $die_error,
5541 - array(
5542 - 'a' => array(
5543 - 'href' => array(),
5544 - ),
5545 - )
5546 - )
5547 - );
5548 - }
5549 -
5550 - switch ( $sanitation ) {
5551 - case 'int':
5552 - $this->json_api_authorization_request[ $key ] = (int) $data->$key;
5553 - break;
5554 - case 'opaque':
5555 - $this->json_api_authorization_request[ $key ] = (string) $data->$key;
5556 - break;
5557 - case 'string':
5558 - $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
5559 - break;
5560 - case 'url':
5561 - $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
5562 - break;
5563 - }
5564 - }
5565 -
5566 - if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5567 - wp_die(
5568 - wp_kses(
5569 - $die_error,
5570 - array(
5571 - 'a' => array(
5572 - 'href' => array(),
5573 - ),
5574 - )
5575 - )
5576 - );
5577 - }
5469 + return ( new Authorize_Json_Api() )->verify_json_api_authorization_request( $environment );
5578 5470 }
5579 5471
5580 5472 /**
5581 5473 * HTML for the JSON API authorization notice.
5582 5474 *
5475 + * @deprecated 13.4
5476 + *
5583 5477 * @return string
5584 5478 */
5585 5479 public function login_message_json_api_authorization() {
5586 - return '<p class="message">' . sprintf(
5587 - /* translators: Name/image of the client requesting authorization */
5588 - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
5589 - '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5590 - ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5480 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::login_message_json_api_authorization' );
5481 +
5482 + return ( new Authorize_Json_Api() )->login_message_json_api_authorization();
5591 5483 }
5592 5484
5593 5485 /**
5594 5486 * Get $content_width, but with a <s>twist</s> filter.
@@ -5632,32 +5524,23 @@
5632 5524
5633 5525 /**
5634 5526 * Checks if the site is currently in an identity crisis.
5635 5527 *
5528 + * Now delegates to the Connection package so this matches what the heartbeat itself reports.
5529 + * Note the package guards on `Connection\Manager::is_connected()` where this used to guard on
5530 + * `Jetpack::is_connection_ready()`, so the `jetpack_is_connection_ready` filter no longer applies.
5531 + *
5532 + * @deprecated 16.2
5533 + *
5636 5534 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
5637 5535 */
5638 5536 public static function check_identity_crisis() {
5639 - if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
5640 - return false;
5641 - }
5537 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Identity_Crisis::check_identity_crisis' );
5642 5538
5643 - return Jetpack_Options::get_option( 'sync_error_idc' );
5539 + return Identity_Crisis::check_identity_crisis();
5644 5540 }
5645 5541
5646 5542 /**
5647 - * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
5648 - *
5649 - * @since 4.4.0
5650 - * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
5651 - *
5652 - * @return bool
5653 - */
5654 - public static function validate_sync_error_idc_option() {
5655 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' );
5656 - return Identity_Crisis::validate_sync_error_idc_option();
5657 - }
5658 -
5659 - /**
5660 5543 * Normalizes a url by doing three things:
5661 5544 * - Strips protocol
5662 5545 * - Strips www
5663 5546 * - Adds a trailing slash
@@ -5678,35 +5561,8 @@
5678 5561 return $url;
5679 5562 }
5680 5563
5681 5564 /**
5682 - * Gets the value that is to be saved in the jetpack_sync_error_idc option.
5683 - *
5684 - * @since 4.4.0
5685 - * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
5686 - * @deprecated 9.8.0 Use \\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option
5687 - *
5688 - * @param array $response HTTP response.
5689 - * @return array Array of the local urls, wpcom urls, and error code
5690 - */
5691 - public static function get_sync_error_idc_option( $response = array() ) {
5692 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' );
5693 - return Identity_Crisis::get_sync_error_idc_option( $response );
5694 - }
5695 -
5696 - /**
5697 - * Returns the value of the jetpack_sync_idc_optin filter, or constant.
5698 - * If set to true, the site will be put into staging mode.
5699 - *
5700 - * @since 4.3.2
5701 - * @return bool
5702 - */
5703 - public static function sync_idc_optin() {
5704 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' );
5705 - return Identity_Crisis::sync_idc_optin();
5706 - }
5707 -
5708 - /**
5709 5565 * Maybe Use a .min.css stylesheet, maybe not.
5710 5566 *
5711 5567 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
5712 5568 *
@@ -5727,9 +5583,9 @@
5727 5583 // Strip out the abspath.
5728 5584 $base = dirname( plugin_basename( $plugin ) );
5729 5585
5730 5586 // Short out on non-Jetpack assets.
5731 - if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
5587 + if ( ! str_starts_with( $base, 'jetpack/' ) ) {
5732 5588 return $url;
5733 5589 }
5734 5590
5735 5591 // File name parsing.
@@ -5769,15 +5625,15 @@
5769 5625 *
5770 5626 * @return mixed
5771 5627 */
5772 5628 public static function set_suffix_on_min( $src, $handle ) {
5773 - if ( false === strpos( $src, '.min.css' ) ) {
5629 + if ( ! is_string( $src ) || ! str_contains( $src, '.min.css' ) ) {
5774 5630 return $src;
5775 5631 }
5776 5632
5777 5633 if ( ! empty( self::$min_assets ) ) {
5778 5634 foreach ( self::$min_assets as $file => $path ) {
5779 - if ( false !== strpos( $src, $file ) ) {
5635 + if ( str_contains( $src, $file ) ) {
5780 5636 wp_style_add_data( $handle, 'suffix', '.min' );
5781 5637 return $src;
5782 5638 }
5783 5639 }
@@ -5807,8 +5663,10 @@
5807 5663 *
5808 5664 * Data passed in with the $data parameter will be available in the
5809 5665 * template file as $data['value']
5810 5666 *
5667 + * @html-template-var array $data
5668 + *
5811 5669 * @param string $template - Template file to load.
5812 5670 * @param array $data - Any data to pass along to the template.
5813 5671 * @return boolean - If template file was found.
5814 5672 **/
@@ -5826,8 +5684,19 @@
5826 5684 return false;
5827 5685 }
5828 5686
5829 5687 /**
5688 + * Register Jetpack-specific tests on the connection package's health test suite.
5689 + *
5690 + * @param \Automattic\Jetpack\Connection\Connection_Health_Tests $connection_tests The test suite instance.
5691 + */
5692 + public function register_jetpack_connection_tests( $connection_tests ) {
5693 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger/class-jetpack-cxn-tests.php';
5694 + $jetpack_tests = new Jetpack_Cxn_Tests();
5695 + $jetpack_tests->register_tests_on( $connection_tests );
5696 + }
5697 +
5698 + /**
5830 5699 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
5831 5700 */
5832 5701 public function deprecated_hooks() {
5833 5702 $filter_deprecated_list = array(
@@ -5954,13 +5823,8 @@
5954 5823 'jetpack_cache_plans' => array(
5955 5824 'replacement' => null,
5956 5825 'version' => 'jetpack-6.1.0',
5957 5826 ),
5958 -
5959 - 'jetpack_lazy_images_skip_image_with_atttributes' => array(
5960 - 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
5961 - 'version' => 'jetpack-6.5.0',
5962 - ),
5963 5827 'jetpack_enable_site_verification' => array(
5964 5828 'replacement' => null,
5965 5829 'version' => 'jetpack-6.5.0',
5966 5830 ),
@@ -6044,8 +5908,22 @@
6044 5908 'jetpack_are_blogging_prompts_enabled' => array(
6045 5909 'replacement' => null,
6046 5910 'version' => 'jetpack-11.8.0',
6047 5911 ),
5912 + 'jetpack_subscriptions_modal_enabled' => array(
5913 + 'replacement' => null,
5914 + 'version' => 'jetpack-12.7.0',
5915 + ),
5916 + 'jetpack_pre_connection_prompt_helpers' => array(
5917 + 'replacement' => null,
5918 + 'version' => 'jetpack-13.2.0',
5919 + ),
5920 + 'jetpack_contact_form_use_package' => array(
5921 + 'replacement' => null,
5922 + 'version' => 'jetpack-13.4.0',
5923 + ),
5924 + // jetpack_implode_frontend_css has been removed, but is not listed here. The updated behavior is exactly the only use of the filter.
5925 + // We can reassess formally deprecating it here later; for now, it would be noise with no functional difference.
6048 5926 );
6049 5927
6050 5928 foreach ( $filter_deprecated_list as $tag => $args ) {
6051 5929 if ( has_filter( $tag ) ) {
@@ -6148,9 +6026,9 @@
6148 6026 foreach ( $matches as $match ) {
6149 6027 $url = trim( $match['path'], "'\" \t" );
6150 6028
6151 6029 // If this is a data url, we don't want to mess with it.
6152 - if ( 'data:' === substr( $url, 0, 5 ) ) {
6030 + if ( str_starts_with( $url, 'data:' ) ) {
6153 6031 continue;
6154 6032 }
6155 6033
6156 6034 // If this is an absolute or protocol-agnostic url,
@@ -6176,113 +6054,8 @@
6176 6054 return $css;
6177 6055 }
6178 6056
6179 6057 /**
6180 - * This methods removes all of the registered css files on the front end
6181 - * from Jetpack in favor of using a single file. In effect "imploding"
6182 - * all the files into one file.
6183 - *
6184 - * Pros:
6185 - * - Uses only ONE css asset connection instead of 15
6186 - * - Saves a minimum of 56k
6187 - * - Reduces server load
6188 - * - Reduces time to first painted byte
6189 - *
6190 - * Cons:
6191 - * - Loads css for ALL modules. However all selectors are prefixed so it
6192 - * should not cause any issues with themes.
6193 - * - Plugins/themes dequeuing styles no longer do anything. See
6194 - * jetpack_implode_frontend_css filter for a workaround
6195 - *
6196 - * For some situations developers may wish to disable css imploding and
6197 - * instead operate in legacy mode where each file loads seperately and
6198 - * can be edited individually or dequeued. This can be accomplished with
6199 - * the following line:
6200 - *
6201 - * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6202 - *
6203 - * @param bool $travis_test Is this a test run.
6204 - *
6205 - * @since 3.2
6206 - */
6207 - public function implode_frontend_css( $travis_test = false ) {
6208 - $do_implode = true;
6209 - if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6210 - $do_implode = false;
6211 - }
6212 -
6213 - // Do not implode CSS when the page loads via the AMP plugin.
6214 - if ( class_exists( Jetpack_AMP_Support::class ) && Jetpack_AMP_Support::is_amp_request() ) {
6215 - $do_implode = false;
6216 - }
6217 -
6218 - /**
6219 - * Allow CSS to be concatenated into a single jetpack.css file.
6220 - *
6221 - * @since 3.2.0
6222 - *
6223 - * @param bool $do_implode Should CSS be concatenated? Default to true.
6224 - */
6225 - $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6226 -
6227 - // Do not use the imploded file when default behavior was altered through the filter.
6228 - if ( ! $do_implode ) {
6229 - return;
6230 - }
6231 -
6232 - // We do not want to use the imploded file in dev mode, or if not connected.
6233 - if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6234 - if ( ! $travis_test ) {
6235 - return;
6236 - }
6237 - }
6238 -
6239 - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen.
6240 - if ( get_option( 'sharedaddy_disable_resources' ) ) {
6241 - return;
6242 - }
6243 -
6244 - /*
6245 - * Now we assume Jetpack is connected and able to serve the single
6246 - * file.
6247 - *
6248 - * In the future there will be a check here to serve the file locally
6249 - * or potentially from the Jetpack CDN
6250 - *
6251 - * For now:
6252 - * - Enqueue a single imploded css file
6253 - * - Zero out the style_loader_tag for the bundled ones
6254 - * - Be happy, drink scotch
6255 - */
6256 -
6257 - add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6258 -
6259 - $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6260 -
6261 - wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6262 - wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6263 - }
6264 -
6265 - /**
6266 - * Removes styles that are part of concatenated group.
6267 - *
6268 - * @param string $tag Style tag.
6269 - * @param string $handle Style handle.
6270 - *
6271 - * @return string
6272 - */
6273 - public function concat_remove_style_loader_tag( $tag, $handle ) {
6274 - if ( in_array( $handle, $this->concatenated_style_handles, true ) ) {
6275 - $tag = '';
6276 - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6277 - $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6278 - }
6279 - }
6280 -
6281 - return $tag;
6282 - }
6283 -
6284 - /**
6285 6058 * Check the heartbeat data
6286 6059 *
6287 6060 * Organizes the heartbeat data by severity. For example, if the site
6288 6061 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
@@ -6294,9 +6067,23 @@
6294 6067 *
6295 6068 * $return array $filtered_data
6296 6069 */
6297 6070 public static function jetpack_check_heartbeat_data() {
6298 - $raw_data = Jetpack_Heartbeat::generate_stats_array();
6071 + /*
6072 + * Site environment stats (incl. wp-version/php-version checked below) now live in the Connection package,
6073 + * and the IDC stat is contributed by the Connection package's `jetpack_heartbeat_stats_array` filter
6074 + * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6075 + * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6076 + * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6077 + */
6078 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
6079 + ? Heartbeat::get_environment_stats()
6080 + : array();
6081 + $raw_data = array_merge(
6082 + Jetpack_Heartbeat::generate_stats_array(),
6083 + $env_stats,
6084 + array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6085 + );
6299 6086
6300 6087 $good = array();
6301 6088 $caution = array();
6302 6089 $bad = array();
@@ -6362,23 +6149,8 @@
6362 6149 return Jetpack_Options::get_options_for_reset();
6363 6150 }
6364 6151
6365 6152 /**
6366 - * Strip http:// or https:// from a url, replaces forward slash with ::,
6367 - * so we can bring them directly to their site in calypso.
6368 - *
6369 - * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6370 - *
6371 - * @param string $url URL.
6372 - * @return string url without the guff.
6373 - */
6374 - public static function build_raw_urls( $url ) {
6375 - _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6376 -
6377 - return ( new Status() )->get_site_suffix( $url );
6378 - }
6379 -
6380 - /**
6381 6153 * Get the user's meta box order.
6382 6154 *
6383 6155 * @param array $sorted Value for the user's option.
6384 6156 * @return mixed
@@ -6388,9 +6160,9 @@
6388 6160 return $sorted;
6389 6161 }
6390 6162
6391 6163 foreach ( $sorted as $box_context => $ids ) {
6392 - if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6164 + if ( ! str_contains( $ids, 'dashboard_stats' ) ) {
6393 6165 // If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6394 6166 continue;
6395 6167 }
6396 6168
@@ -6407,68 +6179,9 @@
6407 6179 }
6408 6180
6409 6181 return $sorted;
6410 6182 }
6411 -
6412 6183 /**
6413 - * Adds a "blank" column in the user admin table to display indication of user connection.
6414 - *
6415 - * @param array $columns User list table columns.
6416 - *
6417 - * @return array
6418 - */
6419 - public function jetpack_icon_user_connected( $columns ) {
6420 - $columns['user_jetpack'] = '';
6421 - return $columns;
6422 - }
6423 -
6424 - /**
6425 - * Show Jetpack icon if the user is linked.
6426 - *
6427 - * @param string $val HTML for the icon.
6428 - * @param string $col User list table column.
6429 - * @param int $user_id User ID.
6430 - *
6431 - * @return string
6432 - */
6433 - public function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
6434 - if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) {
6435 - $jetpack_logo = new Jetpack_Logo();
6436 - $emblem_html = sprintf(
6437 - '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
6438 - esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
6439 - $jetpack_logo->get_jp_emblem()
6440 - );
6441 - return $emblem_html;
6442 - }
6443 -
6444 - return $val;
6445 - }
6446 -
6447 - /**
6448 - * Style the Jetpack user column
6449 - */
6450 - public function jetpack_user_col_style() {
6451 - global $current_screen;
6452 - if ( ! empty( $current_screen->base ) && 'users' === $current_screen->base ) {
6453 - ?>
6454 - <style>
6455 - .fixed .column-user_jetpack {
6456 - width: 21px;
6457 - }
6458 - .jp-emblem-user-admin svg {
6459 - width: 20px;
6460 - height: 20px;
6461 - }
6462 - .jp-emblem-user-admin path {
6463 - fill: #069e08;
6464 - }
6465 - </style>
6466 - <?php
6467 - }
6468 - }
6469 -
6470 - /**
6471 6184 * Checks if Akismet is active and working.
6472 6185 *
6473 6186 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
6474 6187 * that implied usage of methods present since more recent version.
@@ -6560,26 +6273,15 @@
6560 6273 /**
6561 6274 * Return Calypso environment value; used for developing Jetpack and pairing
6562 6275 * it with different Calypso enrionments, such as localhost.
6563 6276 *
6564 - * @since 7.4.0
6277 + * @deprecated 12.4 Moved to the Status package.
6565 6278 *
6566 6279 * @return string Calypso environment
6567 6280 */
6568 6281 public static function get_calypso_env() {
6569 - if ( isset( $_GET['calypso_env'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6570 - return sanitize_key( $_GET['calypso_env'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6571 - }
6572 -
6573 - if ( getenv( 'CALYPSO_ENV' ) ) {
6574 - return sanitize_key( getenv( 'CALYPSO_ENV' ) );
6575 - }
6576 -
6577 - if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
6578 - return sanitize_key( CALYPSO_ENV );
6579 - }
6580 -
6581 - return '';
6282 + _deprecated_function( __METHOD__, 'jetpack-12.4', '\Automattic\Jetpack\Status\Host->get_calypso_env' );
6283 + return ( new Host() )->get_calypso_env();
6582 6284 }
6583 6285
6584 6286 /**
6585 6287 * Returns the hostname with protocol for Calypso.
@@ -6589,9 +6291,9 @@
6589 6291 *
6590 6292 * @return string Calypso host.
6591 6293 */
6592 6294 public static function get_calypso_host() {
6593 - $calypso_env = self::get_calypso_env();
6295 + $calypso_env = ( new Host() )->get_calypso_env();
6594 6296 switch ( $calypso_env ) {
6595 6297 case 'development':
6596 6298 return 'http://calypso.localhost:3000/';
6597 6299 case 'wpcalypso':
@@ -6640,13 +6342,20 @@
6640 6342 }
6641 6343 }
6642 6344
6643 6345 /**
6644 - * Returns a boolean for whether backups UI should be displayed or not.
6346 + * Whether UI for backups should be displayed.
6645 6347 *
6348 + * On WPCom platforms this is gated on the backups-self-serve site feature.
6349 + * On self-hosted Jetpack sites it falls back to the jetpack_show_backups filter.
6350 + *
6646 6351 * @return bool Should backups UI be displayed?
6647 6352 */
6648 6353 public static function show_backups_ui() {
6354 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6355 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'backups-self-serve' );
6356 + }
6357 +
6649 6358 /**
6650 6359 * Whether UI for backups should be displayed.
6651 6360 *
6652 6361 * @since 6.5.0
@@ -6656,8 +6365,24 @@
6656 6365 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
6657 6366 }
6658 6367
6659 6368 /**
6369 + * Whether UI for security scanning should be displayed.
6370 + *
6371 + * On WPCom platforms this is gated on the scan-self-serve site feature.
6372 + * On self-hosted Jetpack sites it always returns true.
6373 + *
6374 + * @return bool Should scan UI be displayed?
6375 + */
6376 + public static function show_scan_ui() {
6377 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6378 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'scan-self-serve' );
6379 + }
6380 +
6381 + return true;
6382 + }
6383 +
6384 + /**
6660 6385 * Clean leftoveruser meta.
6661 6386 *
6662 6387 * Delete Jetpack-related user meta when it is no longer needed.
6663 6388 *
@@ -6729,8 +6454,40 @@
6729 6454 'url' => Redirect::get_url( 'jetpack-faq-backup-disclaimer' ),
6730 6455 ),
6731 6456 );
6732 6457
6458 + $products['creator'] = array(
6459 + 'title' => __( 'Jetpack Creator', 'jetpack' ),
6460 + 'slug' => 'jetpack_creator_yearly',
6461 + 'description' => __( 'Craft stunning content, boost your subscriber base, and monetize your online presence.', 'jetpack' ),
6462 + 'show_promotion' => true,
6463 + 'discount_percent' => 50,
6464 + 'included_in_plans' => array( 'complete' ),
6465 + 'features' => array(
6466 + _x( 'Unlimited subscriber imports', 'Creator Product Feature', 'jetpack' ),
6467 + _x( 'Earn more from your content', 'Creator Product Feature', 'jetpack' ),
6468 + _x( 'Accept payments with PayPal', 'Creator Product Feature', 'jetpack' ),
6469 + _x( 'Increase earnings with WordAds', 'Creator Product Feature', 'jetpack' ),
6470 + ),
6471 + );
6472 +
6473 + $products['growth'] = array(
6474 + 'title' => __( 'Jetpack Growth', 'jetpack' ),
6475 + 'slug' => 'jetpack_growth_yearly',
6476 + 'description' => __( 'Essential tools to help you grow your audience, track visitor engagement, and turn leads into loyal customers and advocates.', 'jetpack' ),
6477 + 'show_promotion' => true,
6478 + 'discount_percent' => 50,
6479 + 'included_in_plans' => array( 'complete' ),
6480 + 'features' => array(
6481 + _x( 'Jetpack Social', 'Growth Product Feature', 'jetpack' ),
6482 + _x( 'Jetpack Stats (10K site views, upgradeable)', 'Growth Product Feature', 'jetpack' ),
6483 + _x( 'Unlimited subscriber imports', 'Growth Product Feature', 'jetpack' ),
6484 + _x( 'Earn more from your content', 'Growth Product Feature', 'jetpack' ),
6485 + _x( 'Accept payments with PayPal', 'Growth Product Feature', 'jetpack' ),
6486 + _x( 'Increase earnings with WordAds', 'Growth Product Feature', 'jetpack' ),
6487 + ),
6488 + );
6489 +
6733 6490 $products['scan'] = array(
6734 6491 'title' => __( 'Jetpack Scan', 'jetpack' ),
6735 6492 'slug' => 'jetpack_scan',
6736 6493 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ),
@@ -6760,9 +6517,9 @@
6760 6517 ),
6761 6518 );
6762 6519
6763 6520 $products['akismet'] = array(
6764 - 'title' => __( 'Akismet Anti-Spam', 'jetpack' ),
6521 + 'title' => __( 'Akismet Anti-spam', 'jetpack' ),
6765 6522 'slug' => 'jetpack_anti_spam',
6766 6523 'description' => __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack' ),
6767 6524 'show_promotion' => true,
6768 6525 'discount_percent' => 50,
@@ -6856,5 +6613,106 @@
6856 6613
6857 6614 return true;
6858 6615 }
6859 6616
6617 + /**
6618 + * Add 5-star review link on the Jetpack Plugin meta, in the plugins list table (on the plugins page).
6619 + *
6620 + * @param array $plugin_meta An array of the plugin's metadata.
6621 + * @param string $plugin_file Path to the plugin file.
6622 + *
6623 + * @return array $plugin_meta An array of the plugin's metadata.
6624 + */
6625 + public function add_5_star_review_link( $plugin_meta, $plugin_file ) {
6626 + if ( $plugin_file !== 'jetpack/jetpack.php' ) {
6627 + return $plugin_meta;
6628 + }
6629 +
6630 + $u = get_current_user_id();
6631 + $site = get_site_url();
6632 +
6633 + $plugin_meta[] = '<a href="https://jetpack.com/redirect?source=jetpack-plugin-review&site=' . esc_attr( $site ) . '&u=' . esc_attr( $u ) . '" target="_blank" rel="noopener noreferrer" title="' . esc_attr__( 'Rate Jetpack on WordPress.org', 'jetpack' ) . '" style="color: #ffb900">'
6634 + . str_repeat( '<span class="dashicons dashicons-star-filled" style="font-size: 16px; width:16px; height: 16px"></span>', 5 )
6635 + . '</a>';
6636 +
6637 + return $plugin_meta;
6638 + }
6639 +
6640 + /**
6641 + * Lazy instantiation of the Plugin_Tracking object.
6642 + *
6643 + * @since 13.9
6644 + *
6645 + * @return void
6646 + */
6647 + public function initialize_tracking() {
6648 + if ( did_action( 'jetpack_initialize_tracking' ) > 1 ) {
6649 + // Only need to run once.
6650 + return;
6651 + }
6652 +
6653 + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) || static::is_connection_ready() ) {
6654 + ( new Plugin_Tracking() )->init();
6655 + }
6656 + }
6657 +
6658 + /**
6659 + * Run the "initialize tracking" hook.
6660 + *
6661 + * @since 13.9
6662 + */
6663 + public function run_initialize_tracking_action() {
6664 + /**
6665 + * Fires when the tracking needs to be initialized.
6666 + * Doesn't necessarily mean that will actually happen, depends if the 'jetpack_tos_agreed' option is set.
6667 + *
6668 + * @since 13.9
6669 + */
6670 + do_action( 'jetpack_initialize_tracking' );
6671 + }
6672 +
6673 + /**
6674 + * Initialize REST jsonAPI if needed.
6675 + *
6676 + * @return void
6677 + */
6678 + public function maybe_initialize_rest_jsonapi() {
6679 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
6680 + if ( ! empty( $_GET['jsonapi'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) {
6681 + require_once ABSPATH . 'wp-admin/includes/admin.php'; // JSON API relies on WP functionality not autoloaded in REST.
6682 +
6683 + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
6684 + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
6685 + }
6686 + }
6687 +
6688 + /**
6689 + * Run plugin post-activation actions if we need to.
6690 + *
6691 + * @return void
6692 + */
6693 + private function plugin_post_activation() {
6694 + if ( ( new Status() )->is_offline_mode() ) {
6695 + return;
6696 + }
6697 +
6698 + if ( get_transient( 'activated_jetpack' ) ) {
6699 + delete_transient( 'activated_jetpack' );
6700 +
6701 + if ( ( new Host() )->is_woa_site() ) {
6702 + $redirect_url = static::admin_url( 'page=jetpack' );
6703 + } elseif ( is_network_admin() ) {
6704 + $redirect_url = admin_url( 'network/admin.php?page=jetpack' );
6705 + } elseif ( get_transient( 'my_jetpack_product_activated' ) ) {
6706 + // don't redirect if this is an activation that just came from My Jetpack
6707 + // My Jetpack has its own set of post-activation redirects
6708 + return;
6709 + } elseif ( My_Jetpack_Initializer::should_initialize() ) {
6710 + $redirect_url = static::admin_url( 'page=my-jetpack' );
6711 + } else {
6712 + $redirect_url = static::admin_url( 'page=jetpack' );
6713 + }
6714 +
6715 + wp_safe_redirect( $redirect_url );
6716 + }
6717 + }
6860 6718 }