PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
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 +1006 -1197 12.2.3 → 16.3-a.5 View file →
@@ -7,32 +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;
12 11 use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
13 12 use Automattic\Jetpack\Config;
13 +use Automattic\Jetpack\Connection\Authorize_Json_Api;
14 14 use Automattic\Jetpack\Connection\Client;
15 -use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
16 15 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
17 -use Automattic\Jetpack\Connection\Nonce_Handler;
18 16 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
19 17 use Automattic\Jetpack\Connection\Secrets;
20 18 use Automattic\Jetpack\Connection\Tokens;
21 -use Automattic\Jetpack\Connection\Utils as Connection_Utils;
19 +use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
22 20 use Automattic\Jetpack\Constants;
23 21 use Automattic\Jetpack\CookieState;
22 +use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
24 23 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
25 24 use Automattic\Jetpack\Errors;
25 +use Automattic\Jetpack\Feature_Policy;
26 26 use Automattic\Jetpack\Files;
27 +use Automattic\Jetpack\Heartbeat;
27 28 use Automattic\Jetpack\Identity_Crisis;
29 +use Automattic\Jetpack\Import\Main as Import_Main;
28 30 use Automattic\Jetpack\Licensing;
29 31 use Automattic\Jetpack\Modules;
30 32 use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
31 -use Automattic\Jetpack\Partner;
33 +use Automattic\Jetpack\Newsletter\Reader_Link;
32 34 use Automattic\Jetpack\Paths;
35 +use Automattic\Jetpack\Plugin\Deprecate;
33 36 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
37 +use Automattic\Jetpack\Podcast\Podcast;
34 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;
35 41 use Automattic\Jetpack\Status;
36 42 use Automattic\Jetpack\Status\Host;
37 43 use Automattic\Jetpack\Status\Visitor;
38 44 use Automattic\Jetpack\Sync\Actions as Sync_Actions;
@@ -39,9 +45,14 @@
39 45 use Automattic\Jetpack\Sync\Health;
40 46 use Automattic\Jetpack\Sync\Sender;
41 47 use Automattic\Jetpack\Terms_Of_Service;
42 48 use Automattic\Jetpack\Tracking;
49 +use Automattic\Woocommerce_Analytics;
43 50
51 +if ( ! defined( 'ABSPATH' ) ) {
52 + exit( 0 );
53 +}
54 +
44 55 /*
45 56 Options:
46 57 jetpack_options (array)
47 58 An array of options.
@@ -76,48 +87,8 @@
76 87 */
77 88 public $xmlrpc_server = null;
78 89
79 90 /**
80 - * The handles of styles that are concatenated into jetpack.css.
81 - *
82 - * When making changes to that list, you must also update concat_list in tools/webpack.config.css.js.
83 - *
84 - * @var array The handles of styles that are concatenated into jetpack.css.
85 - */
86 - public $concatenated_style_handles = array(
87 - 'jetpack-carousel-swiper-css',
88 - 'jetpack-carousel',
89 - 'grunion.css',
90 - 'the-neverending-homepage',
91 - 'jetpack_likes',
92 - 'jetpack_related-posts',
93 - 'sharedaddy',
94 - 'jetpack-slideshow',
95 - 'presentations',
96 - 'quiz',
97 - 'jetpack-subscriptions',
98 - 'jetpack-responsive-videos',
99 - 'jetpack-social-menu',
100 - 'tiled-gallery',
101 - 'jetpack_display_posts_widget',
102 - 'gravatar-profile-widget',
103 - 'goodreads-widget',
104 - 'jetpack_social_media_icons_widget',
105 - 'jetpack-top-posts-widget',
106 - 'jetpack_image_widget',
107 - 'jetpack-my-community-widget',
108 - 'jetpack-authors-widget',
109 - 'wordads',
110 - 'eu-cookie-law-style',
111 - 'flickr-widget-style',
112 - 'jetpack-search-widget',
113 - 'jetpack-simple-payments-widget-style',
114 - 'jetpack-widget-social-icons-styles',
115 - 'wpcom_instagram_widget',
116 - 'milestone-widget',
117 - );
118 -
119 - /**
120 91 * Contains all assets that have had their URL rewritten to minified versions.
121 92 *
122 93 * @var array
123 94 */
@@ -132,11 +103,8 @@
132 103 'contact-form' => array(
133 104 array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
134 105 array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
135 106 ),
136 - 'custom-css' => array(
137 - array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
138 - ),
139 107 'gravatar-hovercards' => array(
140 108 array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
141 109 ),
142 110 'latex' => array(
@@ -297,13 +265,8 @@
297 265 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
298 266 'Rank Math' => 'seo-by-rank-math/rank-math.php',
299 267 'Slim SEO' => 'slim-seo/slim-seo.php',
300 268 ),
301 - 'lazy-images' => array(
302 - 'Lazy Load' => 'lazy-load/lazy-load.php',
303 - 'BJ Lazy Load' => 'bj-lazy-load/bj-lazy-load.php',
304 - 'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
305 - ),
306 269 );
307 270
308 271 /**
309 272 * Plugins for which we turn off our Facebook OG Tags implementation.
@@ -312,9 +275,8 @@
312 275 * Graph tags via filter when their Social Meta modules are active:
313 276 *
314 277 * - All in One SEO Pack, All in one SEO Pack Pro
315 278 * - WordPress SEO by Yoast, WordPress SEO Premium by Yoast
316 - * - SEOPress, SEOPress Pro
317 279 *
318 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:
319 281 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
320 282 *
@@ -357,8 +319,10 @@
357 319 'wp-caregiver/wp-caregiver.php', // WP Caregiver.
358 320 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta.
359 321 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
360 322 'wp-ogp/wp-ogp.php', // WP-OGP.
323 + 'wp-seopress/seopress.php', // SEOPress.
324 + 'wp-seopress-pro/seopress-pro.php', // SEOPress Pro.
361 325 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
362 326 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button.
363 327 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox.
364 328 'seo-by-rank-math/rank-math.php', // Rank Math.
@@ -425,8 +389,10 @@
425 389
426 390 /**
427 391 * Verified data for JSON authorization request
428 392 *
393 + * @deprecated 13.4
394 + *
429 395 * @var array
430 396 */
431 397 public $json_api_authorization_request = array();
432 398
@@ -467,8 +433,16 @@
467 433 */
468 434 public static $instance = false;
469 435
470 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 + /**
471 445 * Singleton
472 446 *
473 447 * @static
474 448 */
@@ -488,17 +462,21 @@
488 462 public function plugin_upgrade() {
489 463 if ( self::is_connection_ready() ) {
490 464 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
491 465 if ( JETPACK__VERSION !== $version ) {
492 - // Prevent multiple upgrades at once - only a single process should trigger
493 - // an upgrade to avoid stampedes.
494 - if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
495 - 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 );
496 477 }
497 478
498 - // Set a short lock to prevent multiple instances of the upgrade.
499 - set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
500 -
501 479 // check which active modules actually exist and remove others from active_modules list.
502 480 $unfiltered_modules = self::get_active_modules();
503 481 $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
504 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
@@ -504,9 +482,9 @@
504 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
505 483 self::update_active_modules( $modules );
506 484 }
507 485
508 - add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
486 + self::register_upgrade_init_hooks();
509 487
510 488 // Upgrade to 4.3.0.
511 489 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
512 490 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
@@ -561,8 +539,16 @@
561 539 Jetpack_Options::delete_option( 'autoupdate_plugins' );
562 540 } // Should we have some type of fallback if something fails here?
563 541 }
564 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 +
565 551 if ( did_action( 'wp_loaded' ) ) {
566 552 self::upgrade_on_load();
567 553 } else {
568 554 add_action(
@@ -596,9 +582,8 @@
596 582 }
597 583
598 584 if (
599 585 class_exists( 'Jetpack_Sitemap_Manager' )
600 - && version_compare( JETPACK__VERSION, '5.3', '>=' )
601 586 ) {
602 587 do_action( 'jetpack_sitemaps_purge_data' );
603 588 }
604 589
@@ -613,9 +598,9 @@
613 598 * Also fires Action hooks for each newly activated and deactivated module.
614 599 *
615 600 * @param array $modules Array of active modules to be saved in options.
616 601 *
617 - * @return $success bool true for success, false for failure.
602 + * @return bool $success true for success, false for failure.
618 603 */
619 604 public static function update_active_modules( $modules ) {
620 605 return ( new Modules() )->update_active( $modules );
621 606 }
@@ -645,10 +630,10 @@
645 630 sort( $taken_priorities );
646 631
647 632 $first_priority = array_shift( $taken_priorities );
648 633
649 - if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
650 - $new_priority = - PHP_INT_MAX;
634 + if ( $first_priority <= PHP_INT_MIN ) {
635 + $new_priority = PHP_INT_MIN;
651 636 } else {
652 637 $new_priority = $first_priority - 1;
653 638 }
654 639
@@ -669,31 +654,17 @@
669 654 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
670 655 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
671 656 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
672 657
673 - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
674 -
675 - add_filter(
676 - 'jetpack_signature_check_token',
677 - array( __CLASS__, 'verify_onboarding_token' ),
678 - 10,
679 - 3
680 - );
681 -
682 658 /**
683 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.
684 663 */
685 664 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
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,147 @@
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 + * Whether the bundled Stats v2 dashboard is enabled.
844 + *
845 + * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
846 + * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
847 + * menu alongside the existing Stats UI; it never replaces or hides the
848 + * legacy Stats menu, admin-bar entries, post-list column, or WP dashboard
849 + * widget. The Stats module's tracking is unaffected either way, and Stats v2
850 + * reads what that module collects, so while the module is off the plugin
851 + * answers false here before even reading the flag.
852 + *
853 + * The package has to be loadable for this to be true, so a site with the
854 + * flag on but a missing package answers false here and never adds the
855 + * Stats v2 menu (a warning is logged instead).
856 + *
857 + * @since 16.1
858 + *
859 + * @return bool
860 + */
861 + public static function is_premium_analytics_enabled() {
862 + if ( null !== self::$premium_analytics_enabled ) {
863 + return self::$premium_analytics_enabled;
864 + }
865 +
866 + if ( ! self::is_module_active( 'stats' ) ) {
867 + self::$premium_analytics_enabled = false;
868 + return false;
869 + }
870 +
871 + /**
872 + * Filters whether the bundled Premium Analytics dashboard is enabled.
873 + *
874 + * Resolved once, from `Jetpack::configure()` on `plugins_loaded`, and only
875 + * while the Stats module is active. Register this from a mu-plugin or a
876 + * plugin's main file — a callback added on `plugins_loaded` or later runs
877 + * too late to be seen.
878 + *
879 + * @since 16.1
880 + *
881 + * @param bool $enabled Defaults to the `jetpack_premium_analytics_enabled` option (false).
882 + */
883 + $flag = (bool) apply_filters( 'jetpack_premium_analytics_enabled', (bool) get_option( 'jetpack_premium_analytics_enabled' ) );
884 +
885 + self::$premium_analytics_enabled = $flag && class_exists( 'Automattic\Jetpack\PremiumAnalytics\Analytics' );
886 +
887 + if ( $flag && ! self::$premium_analytics_enabled ) {
888 + wp_trigger_error(
889 + __METHOD__,
890 + 'The jetpack_premium_analytics_enabled flag is on but the Premium Analytics package is not loadable; keeping the Stats UI in place.'
891 + );
892 + }
893 +
894 + return self::$premium_analytics_enabled;
895 + }
896 +
897 + /**
898 + * Expose the setting that turns the Premium Analytics dashboard on and off.
899 + *
900 + * Deliberately not behind is_premium_analytics_enabled(): this is the setting that flips that
901 + * check, so it has to answer while the dashboard is still off.
902 + *
903 + * @since 16.2
904 + *
905 + * @return void
906 + */
907 + public static function register_premium_analytics_enablement_setting() {
908 + if ( class_exists( 'Automattic\Jetpack\PremiumAnalytics\Enablement_Setting' ) ) {
909 + \Automattic\Jetpack\PremiumAnalytics\Enablement_Setting::register();
910 + }
911 + }
912 +
913 + /**
839 914 * Before everything else starts getting initalized, we need to initialize Jetpack using the
840 915 * Config object.
841 916 */
842 917 public function configure() {
@@ -843,16 +918,12 @@
843 918 $config = new Config();
844 919
845 920 foreach (
846 921 array(
847 - 'blaze',
848 922 'jitm',
849 923 'sync',
924 + 'account_protection',
850 925 'waf',
851 - 'videopress',
852 - 'stats',
853 - 'stats_admin',
854 - 'import',
855 926 )
856 927 as $feature
857 928 ) {
858 929 $config->ensure( $feature );
@@ -857,8 +928,103 @@
857 928 ) {
858 929 $config->ensure( $feature );
859 930 }
860 931
932 + // Enable the VideoPress admin UI (the "Jetpack > VideoPress" dashboard) inside the
933 + // Jetpack plugin, mirroring the standalone Jetpack VideoPress plugin. The dashboard
934 + // only renders when the VideoPress module is active (Status::is_active()); when it
935 + // is not, the menu item links to the My Jetpack interstitial to activate it.
936 + $config->ensure( 'videopress', array( 'admin_ui' => true ) );
937 +
938 + /*
939 + * The Import package only registers `jetpack/v4/import` REST routes — it
940 + * does nothing when rendering a front-end page — so gate its `ensure()`
941 + * to keep its PHP out of opcache on the front-end GET hot path. It still
942 + * loads on admin, cron, POST, and WP-CLI requests, and on `rest_api_init`
943 + * for REST: a REST request can't be identified yet at `plugins_loaded`
944 + * (this runs before `Config::on_plugins_loaded`, and `rest_api_init`
945 + * fires later), so it is initialized directly when that hook fires, while
946 + * a plain page view never fires it and so loads nothing.
947 + *
948 + * JITM stays eager (above): unlike Import, its `register()` adds a
949 + * `jetpack_sync_before_send_updated_option` filter that records the
950 + * `jetpack_last_plugin_sync` transient, and a Jetpack Sync send can fire
951 + * on a plain front-end GET — including the dedicated-sync `spawn-sync`
952 + * GET, which runs on `init` and exits before `rest_api_init`. Deferring
953 + * JITM would skip that bookkeeping and leave its message cache stale after
954 + * a plugin change, so it loads on every request as before.
955 + */
956 + if ( self::should_eager_load_packages() ) {
957 + $config->ensure( 'import' );
958 + } else {
959 + add_action(
960 + 'rest_api_init',
961 + array( __CLASS__, 'configure_import_package' ),
962 + 0
963 + );
964 + }
965 +
966 + /*
967 + * The Stats and Stats Admin packages only do work when the Stats module
968 + * is active (the front-end tracking pixel) or on wp-admin, REST, cron,
969 + * POST, and WP-CLI requests: the Stats dashboard page, the stats /
970 + * stats-app REST endpoints (which the block editor also calls for
971 + * email-open rates), the transient-cleanup cron, the connection
972 + * package's package-version tracker (which runs on POSTs and reads the
973 + * `jetpack_package_versions` filter that Stats registers), and CLI
974 + * introspection such as the heartbeat inspector. On a plain front-end
975 + * GET page view with the module off they are inert: the pixel
976 + * short-circuits on `Stats\Main::should_track()` and every other entry
977 + * point only hooks rest_api_init, admin, cron, the POST-only tracker, or
978 + * is reached through WP-CLI.
979 + * Skip loading them — and eagerly constructing the Stats Admin REST
980 + * controller — on that hot path to keep their PHP out of opcache, but
981 + * keep loading them everywhere else exactly as before so the stats REST
982 + * permission mapping (view_stats, registered by Stats\Main), the
983 + * editor's stats-app calls, the cleanup cron, and the package-version
984 + * tracker are all unchanged.
985 + *
986 + * REST requests are not yet identifiable here (REST_REQUEST is defined
987 + * after plugins_loaded), so defer those to rest_api_init. Call the
988 + * package initializers directly rather than `$config->ensure()`: ensure()
989 + * only flags a feature for `Config::on_plugins_loaded()` (plugins_loaded
990 + * priority 2), which has already run by the time rest_api_init fires.
991 + * Priority 0 runs before each package's own priority-10 route
992 + * registration, so their routes still register within the same dispatch.
993 + * A plain page view never fires rest_api_init, so the packages stay
994 + * unloaded there. See JETPACK-1747.
995 + */
996 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'];
997 + $is_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
998 +
999 + if ( self::is_module_active( 'stats' ) || is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli ) {
1000 + $config->ensure( 'stats' );
1001 + $config->ensure( 'stats_admin' );
1002 + } else {
1003 + add_action(
1004 + 'rest_api_init',
1005 + static function () {
1006 + if ( class_exists( 'Automattic\Jetpack\Stats\Main' ) ) {
1007 + \Automattic\Jetpack\Stats\Main::init();
1008 + }
1009 + if ( class_exists( 'Automattic\Jetpack\Stats_Admin\Main' ) ) {
1010 + \Automattic\Jetpack\Stats_Admin\Main::init();
1011 + }
1012 + },
1013 + 0
1014 + );
1015 + }
1016 +
1017 + // Stats v2 (WOOA7S-1595). Unlike Stats above it cannot be deferred when enabled — see
1018 + // Analytics::init() for why, and for why it takes no menu_title here.
1019 + if ( self::is_premium_analytics_enabled() ) {
1020 + \Automattic\Jetpack\PremiumAnalytics\Analytics::init();
1021 + }
1022 +
1023 + // Outside the check above on purpose — see Enablement_Setting. Deferred like Stats, to keep
1024 + // the autoload off the front-end hot path.
1025 + add_action( 'rest_api_init', array( __CLASS__, 'register_premium_analytics_enablement_setting' ), 0 );
1026 +
861 1027 $config->ensure(
862 1028 'connection',
863 1029 array(
864 1030 'slug' => 'jetpack',
@@ -876,12 +1042,8 @@
876 1042 );
877 1043
878 1044 $config->ensure( 'search' );
879 1045
880 - if ( defined( 'ENABLE_WORDADS_SHARED_UI' ) && ENABLE_WORDADS_SHARED_UI ) {
881 - $config->ensure( 'wordads' );
882 - }
883 -
884 1046 if ( ! $this->connection_manager ) {
885 1047 $this->connection_manager = new Connection_Manager( 'jetpack' );
886 1048 }
887 1049
@@ -889,8 +1051,10 @@
889 1051 if ( $modules->is_active( 'publicize' ) && $this->connection_manager->has_connected_user() ) {
890 1052 $config->ensure( 'publicize' );
891 1053 }
892 1054
1055 + add_action( 'jetpack_initialize_tracking', array( $this, 'initialize_tracking' ) );
1056 +
893 1057 /*
894 1058 * Load things that should only be in Network Admin.
895 1059 *
896 1060 * For now blow away everything else until a more full
@@ -905,8 +1069,9 @@
905 1069 $is_connection_ready = self::is_connection_ready();
906 1070
907 1071 if ( $is_connection_ready ) {
908 1072 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
1073 + $this->run_initialize_tracking_action();
909 1074
910 1075 Jetpack_Heartbeat::init();
911 1076 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
912 1077 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
@@ -911,8 +1076,19 @@
911 1076 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
912 1077 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
913 1078 Jetpack_Search_Performance_Logger::init();
914 1079 }
1080 + } else {
1081 + add_action( 'jetpack_agreed_to_terms_of_service', array( $this, 'run_initialize_tracking_action' ) );
1082 + add_action( 'rest_api_init', array( $this, 'run_initialize_tracking_action' ) );
1083 + add_filter(
1084 + 'xmlrpc_methods',
1085 + function ( $methods ) {
1086 + $this->run_initialize_tracking_action();
1087 + return $methods;
1088 + },
1089 + 1
1090 + );
915 1091 }
916 1092
917 1093 // Initialize remote file upload request handlers.
918 1094 $this->add_remote_request_handlers();
@@ -922,20 +1098,10 @@
922 1098 */
923 1099 if ( $is_connection_ready ) {
924 1100 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
925 1101 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 );
1102 + add_action( 'rest_api_init', array( $this, 'maybe_initialize_rest_jsonapi' ) );
928 1103 }
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 1104 }
939 1105
940 1106 /**
941 1107 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
@@ -943,17 +1109,58 @@
943 1109 *
944 1110 * @action plugins_loaded
945 1111 */
946 1112 public function late_initialization() {
947 - add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
1113 + add_action( 'after_setup_theme', array( 'Jetpack', 'load_modules' ), -2 );
948 1114
949 - Partner::init();
950 - My_Jetpack_Initializer::init();
1115 + /*
1116 + * My Jetpack is a wp-admin dashboard. Its Initializer::init() only wires
1117 + * up admin-menu, admin_init, and rest_api_init surfaces — and eagerly
1118 + * loads every product class (backup, boost, protect, …) just to register
1119 + * admin plugin-action links — so none of it is needed on a plain
1120 + * front-end GET page view. (The pieces that do immediate work, e.g.
1121 + * Connection REST authentication and Licensing, are already initialized
1122 + * unconditionally in Jetpack's constructor, so they are unaffected here.)
1123 + *
1124 + * Gate the call to the request types where My Jetpack actually does work.
1125 + * REST can't be detected yet at plugins_loaded, so initialize on
1126 + * rest_api_init for that branch; a plain page view never fires it, so My
1127 + * Jetpack stays unloaded there.
1128 + */
1129 + if ( self::should_eager_load_packages() ) {
1130 + My_Jetpack_Initializer::init();
1131 + } else {
1132 + add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1133 + }
951 1134
952 - // Initialize Boost Speed Score
953 - $modules = Jetpack_Boost_Modules::init();
954 - new Speed_Score( $modules, 'jetpack-dashboard' );
1135 + Scan_Page_Init::initialize();
1136 + Jetpack_SEO_Initializer::init();
955 1137
1138 + if ( ( new Modules() )->is_active( 'podcast' ) ) {
1139 + Podcast::init();
1140 + }
1141 +
1142 + /*
1143 + * Initialize Boost Speed Score. It only does work on REST requests (the
1144 + * dashboard speed-score endpoints) and on a few Jetpack Boost lifecycle
1145 + * actions, so defer constructing it — and loading the boost-speed-score
1146 + * package classes — until one of those hooks actually fires instead of on
1147 + * every request. Priority 0 ensures the object's own callbacks (added in
1148 + * its constructor at the default priority) still run for the firing hook.
1149 + */
1150 + $initialize_speed_score = static function () {
1151 + static $initialized = false;
1152 + if ( $initialized ) {
1153 + return;
1154 + }
1155 + $initialized = true;
1156 + new Speed_Score( array(), 'jetpack-dashboard' );
1157 + };
1158 + add_action( 'rest_api_init', $initialize_speed_score, 0 );
1159 + add_action( 'jetpack_boost_deactivate', $initialize_speed_score, 0 );
1160 + add_action( 'jetpack_boost_environment_changed', $initialize_speed_score, 0 );
1161 + add_action( 'handle_environment_change', $initialize_speed_score, 0 );
1162 +
956 1163 /**
957 1164 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
958 1165 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
959 1166 *
@@ -991,8 +1198,10 @@
991 1198
992 1199 /**
993 1200 * Redirect edit post links to Calypso.
994 1201 *
1202 + * @deprecated since 13.9
1203 + *
995 1204 * @param string $default_url Post edit URL.
996 1205 * @param int $post_id Post ID.
997 1206 *
998 1207 * @return string
@@ -997,8 +1206,10 @@
997 1206 *
998 1207 * @return string
999 1208 */
1000 1209 public function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1210 + _deprecated_function( __METHOD__, '13.9' );
1211 +
1001 1212 $post = get_post( $post_id );
1002 1213
1003 1214 if ( empty( $post ) ) {
1004 1215 return $default_url;
@@ -1029,13 +1240,17 @@
1029 1240
1030 1241 /**
1031 1242 * Redirect edit comment links to Calypso.
1032 1243 *
1244 + * @deprecated since 13.9
1245 + *
1033 1246 * @param string $url Comment edit URL.
1034 1247 *
1035 1248 * @return string
1036 1249 */
1037 1250 public function point_edit_comment_links_to_calypso( $url ) {
1251 + _deprecated_function( __METHOD__, '13.9' );
1252 +
1038 1253 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1039 1254 $query_args = null;
1040 1255 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1041 1256
@@ -1054,30 +1269,16 @@
1054 1269 *
1055 1270 * @return array list of callables.
1056 1271 */
1057 1272 public function filter_sync_callable_whitelist( $callables ) {
1058 -
1059 1273 // Jetpack Functions.
1060 1274 $jetpack_callables = array(
1061 1275 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1062 1276 'updates' => array( 'Jetpack', 'get_updates' ),
1063 1277 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1278 + 'theme_styles' => array( 'Automattic\\Jetpack\\Plugin\\Theme_Styles_Sync', 'get_theme_styles' ),
1064 1279 );
1065 - $callables = array_merge( $callables, $jetpack_callables );
1066 -
1067 - // Jetpack_SSO_Helpers.
1068 - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1069 - $sso_helpers = array(
1070 - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1071 - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1072 - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1073 - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1074 - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1075 - );
1076 - $callables = array_merge( $callables, $sso_helpers );
1077 - }
1078 -
1079 - return $callables;
1280 + return array_merge( $callables, $jetpack_callables );
1080 1281 }
1081 1282
1082 1283 /**
1083 1284 * Extend Sync multisite callables with Jetpack Plugin functions.
@@ -1102,41 +1303,8 @@
1102 1303 return $callables;
1103 1304 }
1104 1305
1105 1306 /**
1106 - * Deprecated
1107 - * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1108 - *
1109 - * @param array $params The action parameters.
1110 - *
1111 - * @deprecated since 9.8.
1112 - */
1113 - public function jetpack_track_last_sync_callback( $params ) {
1114 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1115 - return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1116 - }
1117 -
1118 - /**
1119 - * Jetpack Connection banner callback function.
1120 - *
1121 - * @return void
1122 - */
1123 - public function jetpack_connection_banner_callback() {
1124 - check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1125 -
1126 - // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1127 - if (
1128 - isset( $_REQUEST['dismissBanner'] ) &&
1129 - ! Jetpack_Connection_Banner::force_display()
1130 - ) {
1131 - Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1132 - wp_send_json_success();
1133 - }
1134 -
1135 - wp_die();
1136 - }
1137 -
1138 - /**
1139 1307 * If there are any stats that need to be pushed, but haven't been, push them now.
1140 1308 */
1141 1309 public function push_stats() {
1142 1310 if ( ! empty( $this->stats ) ) {
@@ -1151,16 +1319,8 @@
1151 1319 * @param string $cap Capability name.
1152 1320 */
1153 1321 public function jetpack_custom_caps( $caps, $cap ) {
1154 1322 switch ( $cap ) {
1155 - case 'jetpack_manage_modules':
1156 - case 'jetpack_activate_modules':
1157 - case 'jetpack_deactivate_modules':
1158 - $caps = array( 'manage_options' );
1159 - break;
1160 - case 'jetpack_configure_modules':
1161 - $caps = array( 'manage_options' );
1162 - break;
1163 1323 case 'jetpack_manage_autoupdates':
1164 1324 $caps = array(
1165 1325 'manage_options',
1166 1326 'update_plugins',
@@ -1178,9 +1338,9 @@
1178 1338 if ( $is_offline_mode ) {
1179 1339 $caps = array( 'manage_options' );
1180 1340 break;
1181 1341 } else {
1182 - $caps = array( 'read' );
1342 + $caps = array( 'edit_posts' );
1183 1343 }
1184 1344 break;
1185 1345 }
1186 1346 return $caps;
@@ -1339,9 +1499,9 @@
1339 1499 }
1340 1500 /**
1341 1501 * Does the network allow admins to add new users.
1342 1502 *
1343 - * @return boolian
1503 + * @return bool
1344 1504 */
1345 1505 public static function network_add_new_users() {
1346 1506 return (bool) get_site_option( 'add_new_users' );
1347 1507 }
@@ -1348,9 +1508,9 @@
1348 1508 /**
1349 1509 * File upload psace left per site in MB.
1350 1510 * -1 means NO LIMIT.
1351 1511 *
1352 - * @return number
1512 + * @return int
1353 1513 */
1354 1514 public static function network_site_upload_space() {
1355 1515 // value in MB.
1356 1516 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
@@ -1367,9 +1527,9 @@
1367 1527
1368 1528 /**
1369 1529 * Maximum file upload size set by the network.
1370 1530 *
1371 - * @return number
1531 + * @return int
1372 1532 */
1373 1533 public static function network_max_upload_file_size() {
1374 1534 // value in KB.
1375 1535 return get_site_option( 'fileupload_maxk', 300 );
@@ -1528,9 +1688,9 @@
1528 1688 }
1529 1689 return 0;
1530 1690 }
1531 1691
1532 -// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1692 + // phpcs:disable WordPress.WP.CapitalPDangit.MisspelledInComment
1533 1693 /**
1534 1694 * Gets updates and stores in jetpack_updates.
1535 1695 *
1536 1696 * The jetpack_updates option is saved in the following schema:
@@ -1546,8 +1706,9 @@
1546 1706 *
1547 1707 * @return array
1548 1708 */
1549 1709 public static function get_updates() {
1710 + $updates = array();
1550 1711 $update_data = wp_get_update_data();
1551 1712
1552 1713 // Stores the individual update counts as well as the total count.
1553 1714 if ( isset( $update_data['counts'] ) ) {
@@ -1560,11 +1721,11 @@
1560 1721 if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1561 1722 $updates['wp_update_version'] = $cur->current;
1562 1723 }
1563 1724 }
1564 - return isset( $updates ) ? $updates : array();
1725 + return $updates;
1565 1726 }
1566 - // phpcs:enable
1727 + // phpcs:enable WordPress.WP.CapitalPDangit.MisspelledInComment
1567 1728
1568 1729 /**
1569 1730 * Get update details for core, plugins, and themes.
1570 1731 *
@@ -1622,44 +1783,8 @@
1622 1783 return apply_filters( 'jetpack_is_connection_ready', self::connection()->is_connected(), self::connection() );
1623 1784 }
1624 1785
1625 1786 /**
1626 - * Deprecated: Is Jetpack in development (offline) mode?
1627 - *
1628 - * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1629 - * and themes still use it, and we do not want to flood them with notices.
1630 - *
1631 - * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1632 - *
1633 - * @deprecated since 8.0.
1634 - */
1635 - public static function is_development_mode() {
1636 - _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1637 - return ( new Status() )->is_offline_mode();
1638 - }
1639 -
1640 - /**
1641 - * Whether the site is currently onboarding or not.
1642 - * A site is considered as being onboarded if it currently has an onboarding token.
1643 - *
1644 - * @since 5.8
1645 - * @deprecated Use \Automattic\Jetpack\Status()->is_onboarding()
1646 - *
1647 - * @access public
1648 - * @static
1649 - *
1650 - * @return bool True if the site is currently onboarding, false otherwise
1651 - */
1652 - public static function is_onboarding() {
1653 - _deprecated_function( __METHOD__, 'jetpack-10.9', 'Automattic\\Jetpack\\Status\\is_onboarding' );
1654 -
1655 - if ( ! method_exists( 'Automattic\Jetpack\Status', 'is_onboarding' ) ) {
1656 - return Jetpack_Options::get_option( 'onboarding' ) !== false;
1657 - }
1658 - return ( new Status() )->is_onboarding();
1659 - }
1660 -
1661 - /**
1662 1787 * Determines reason for Jetpack offline mode.
1663 1788 */
1664 1789 public static function development_mode_trigger_text() {
1665 1790 $status = new Status();
@@ -1673,11 +1798,10 @@
1673 1798 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1674 1799 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1675 1800 } elseif ( $status->is_local_site() ) {
1676 1801 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1677 - /** This filter is documented in packages/status/src/class-status.php */
1678 - } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1679 - $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1802 + } elseif ( get_option( 'jetpack_offline_mode' ) ) {
1803 + $notice = __( 'The jetpack_offline_mode option is set to true.', 'jetpack' );
1680 1804 } else {
1681 1805 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1682 1806 }
1683 1807
@@ -1692,9 +1816,9 @@
1692 1816 if ( ( new Status() )->is_offline_mode() ) {
1693 1817 $notice = sprintf(
1694 1818 /* translators: %s is a URL */
1695 1819 __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1696 - Redirect::get_url( 'jetpack-support-development-mode' )
1820 + esc_url( Redirect::get_url( 'jetpack-support-development-mode' ) )
1697 1821 );
1698 1822
1699 1823 $notice .= ' ' . self::development_mode_trigger_text();
1700 1824
@@ -1703,19 +1827,12 @@
1703 1827
1704 1828 // Throw up a notice if using a development version and as for feedback.
1705 1829 if ( self::is_development_version() ) {
1706 1830 /* translators: %s is a URL */
1707 - $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' ) );
1831 + $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' ) ) );
1708 1832
1709 1833 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1710 1834 }
1711 - // Throw up a notice if using staging mode.
1712 - if ( ( new Status() )->is_staging_site() ) {
1713 - /* translators: %s is a URL */
1714 - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1715 -
1716 - echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1717 - }
1718 1835 }
1719 1836
1720 1837 /**
1721 1838 * Whether Jetpack's version maps to a public release, or a development version.
@@ -1736,28 +1853,8 @@
1736 1853 );
1737 1854 }
1738 1855
1739 1856 /**
1740 - * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1741 - *
1742 - * @param int $user_id User ID or will use get_current_user_id if false/not provided.
1743 - */
1744 - public static function is_user_connected( $user_id = false ) {
1745 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1746 - return self::connection()->is_user_connected( $user_id );
1747 - }
1748 -
1749 - /**
1750 - * Get the wpcom user data of the current|specified connected user.
1751 - *
1752 - * @param null|int $user_id User ID or will use get_current_user_id if null.
1753 - */
1754 - public static function get_connected_user_data( $user_id = null ) {
1755 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1756 - return self::connection()->get_connected_user_data( $user_id );
1757 - }
1758 -
1759 - /**
1760 1857 * Get the wpcom email of the current|specified connected user.
1761 1858 *
1762 1859 * @param null|int $user_id User ID or will use get_current_user_id if null.
1763 1860 */
@@ -1809,18 +1906,11 @@
1809 1906 */
1810 1907 public static function load_modules() {
1811 1908 $status = new Status();
1812 1909
1813 - if ( method_exists( $status, 'is_onboarding' ) ) {
1814 - $is_onboarding = $status->is_onboarding();
1815 - } else {
1816 - $is_onboarding = self::is_onboarding();
1817 - }
1818 -
1819 1910 if (
1820 1911 ! self::is_connection_ready()
1821 1912 && ! $status->is_offline_mode()
1822 - && ! $is_onboarding
1823 1913 && (
1824 1914 ! is_multisite()
1825 1915 || ! get_site_option( 'jetpack_protect_active' )
1826 1916 )
@@ -1941,22 +2031,14 @@
1941 2031 *
1942 2032 * @todo Store the result in core's object cache maybe?
1943 2033 */
1944 2034 public static function get_active_plugins() {
1945 - $active_plugins = (array) get_option( 'active_plugins', array() );
1946 -
1947 - if ( is_multisite() ) {
1948 - // Due to legacy code, active_sitewide_plugins stores them in the keys,
1949 - // whereas active_plugins stores them in the values.
1950 - $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1951 - if ( $network_plugins ) {
1952 - $active_plugins = array_merge( $active_plugins, $network_plugins );
1953 - }
2035 + // Older Connection copies can load first and lack this method.
2036 + if ( ! method_exists( Heartbeat::class, 'get_active_plugins' ) ) {
2037 + return array();
1954 2038 }
1955 2039
1956 - sort( $active_plugins );
1957 -
1958 - return array_unique( $active_plugins );
2040 + return Heartbeat::get_active_plugins();
1959 2041 }
1960 2042
1961 2043 /**
1962 2044 * Gets and parses additional plugin data to send with the heartbeat data
@@ -2094,8 +2176,10 @@
2094 2176 *
2095 2177 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2096 2178 */
2097 2179 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2180 + // @todo Remove this require once the deprecated Jetpack_Twitter_Cards wrapper has been removed.
2181 + // Twitter Cards functionality now lives in the jetpack-post-media package (Automattic\Jetpack\Post_Media\Twitter_Cards).
2098 2182 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2099 2183 }
2100 2184 }
2101 2185
@@ -2198,9 +2282,9 @@
2198 2282 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.
2199 2283 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2200 2284 }
2201 2285 wp_safe_redirect( self::admin_url( 'page=' . rawurlencode( $page ) ) );
2202 - exit;
2286 + exit( 0 );
2203 2287 }
2204 2288 }
2205 2289
2206 2290 /**
@@ -2247,8 +2331,12 @@
2247 2331 default:
2248 2332 break;
2249 2333 }
2250 2334 }
2335 + if ( method_exists( Feature_Policy::class, 'ensure_hooks' ) ) {
2336 + Feature_Policy::ensure_hooks();
2337 + }
2338 +
2251 2339 /**
2252 2340 * Filters the array of default modules.
2253 2341 *
2254 2342 * @since 2.5.0
@@ -2280,12 +2368,14 @@
2280 2368 * @return array
2281 2369 */
2282 2370 public function handle_deprecated_modules( $modules ) {
2283 2371 $deprecated_modules = array(
2284 - 'debug' => null, // Closed out and moved to the debugger library.
2285 - 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2286 - 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2287 - 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2372 + 'debug' => null, // Closed out and moved to the debugger library.
2373 + 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2374 + 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2375 + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2376 + 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2377 + 'enhanced-distribution' => null, // Closed out in 13.3 -- WP.com is winding down the firehose.
2288 2378 );
2289 2379
2290 2380 // Don't activate SSO if they never completed activating WPCC.
2291 2381 if ( self::is_module_active( 'wpcc' ) ) {
@@ -2339,8 +2429,17 @@
2339 2429 }
2340 2430 }
2341 2431 }
2342 2432
2433 + // Special case to convert block setting to a block module.
2434 + $block_key = array_search( 'blocks', $modules, true );
2435 + if ( $block_key !== false ) { // Only care if 'blocks' made it through the previous filters.
2436 + $block_option = get_option( 'jetpack_blocks_disabled', null );
2437 + if ( $block_option ) {
2438 + unset( $modules[ $block_key ] );
2439 + }
2440 + }
2441 +
2343 2442 return $modules;
2344 2443 }
2345 2444
2346 2445 /**
@@ -2397,23 +2496,30 @@
2397 2496
2398 2497 /**
2399 2498 * Return module name translation. Uses matching string created in modules/module-headings.php.
2400 2499 *
2500 + * The module list is globbed from `modules/` at runtime, so a module can be listed with no
2501 + * entry in that generated file. Fall back to the untranslated header rather than overwriting
2502 + * it with the null `jetpack_get_module_i18n()` returns for an unknown slug.
2503 + *
2401 2504 * @since 3.9.2
2402 2505 *
2403 2506 * @param array $modules Array of Jetpack modules.
2404 2507 *
2405 - * @return string|void
2508 + * @return array
2406 2509 */
2407 2510 public static function get_translated_modules( $modules ) {
2408 2511 foreach ( $modules as $index => $module ) {
2409 2512 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2410 - if ( isset( $module['name'] ) ) {
2411 - $modules[ $index ]['name'] = $i18n_module['name'];
2513 + $name = $i18n_module['name'] ?? null;
2514 + $description = $i18n_module['description'] ?? null;
2515 +
2516 + if ( null !== $name && isset( $module['name'] ) ) {
2517 + $modules[ $index ]['name'] = $name;
2412 2518 }
2413 - if ( isset( $module['description'] ) ) {
2414 - $modules[ $index ]['description'] = $i18n_module['description'];
2415 - $modules[ $index ]['short_description'] = $i18n_module['description'];
2519 + if ( null !== $description && isset( $module['description'] ) ) {
2520 + $modules[ $index ]['description'] = $description;
2521 + $modules[ $index ]['short_description'] = $description;
2416 2522 }
2417 2523 if ( isset( $module['module_tags'] ) ) {
2418 2524 $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2419 2525 }
@@ -2453,20 +2559,28 @@
2453 2559
2454 2560 /**
2455 2561 * Catches PHP errors. Must be used in conjunction with output buffering.
2456 2562 *
2563 + * @deprecated since 13.5
2457 2564 * @param bool $catch True to start catching, False to stop.
2458 2565 *
2459 2566 * @static
2567 + * @deprecated 13.5
2568 + * @see \Automattic\Jetpack\Errors
2460 2569 */
2461 2570 public static function catch_errors( $catch ) {
2571 + _deprecated_function( __METHOD__, '13.5' );
2572 + // @phan-suppress-next-line PhanDeprecatedClass
2462 2573 return ( new Errors() )->catch_errors( $catch );
2463 2574 }
2464 2575
2465 2576 /**
2466 2577 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2578 + *
2579 + * @deprecated since 13.5
2467 2580 */
2468 2581 public static function catch_errors_on_shutdown() {
2582 + _deprecated_function( __METHOD__, '13.5' );
2469 2583 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2470 2584 }
2471 2585
2472 2586 /**
@@ -2570,9 +2684,9 @@
2570 2684 ),
2571 2685 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2572 2686 );
2573 2687 wp_safe_redirect( $url );
2574 - exit;
2688 + exit( 0 );
2575 2689 }
2576 2690 }
2577 2691
2578 2692 /**
@@ -2590,9 +2704,8 @@
2590 2704
2591 2705 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating.
2592 2706 if ( $send_state_messages ) {
2593 2707 self::restate();
2594 - self::catch_errors( true );
2595 2708 }
2596 2709
2597 2710 $active = self::get_active_modules();
2598 2711
@@ -2660,10 +2773,8 @@
2660 2773 if ( $send_state_messages ) {
2661 2774 self::state( 'error', false );
2662 2775 self::state( 'module', false );
2663 2776 }
2664 -
2665 - self::catch_errors( false );
2666 2777 /**
2667 2778 * Fires when default modules are activated.
2668 2779 *
2669 2780 * @since 1.9.0
@@ -2721,9 +2832,9 @@
2721 2832 * @return string $url module configuration URL.
2722 2833 */
2723 2834 public static function module_configuration_url( $module ) {
2724 2835 $module = self::get_module_slug( $module );
2725 - $default_url = self::admin_url() . "#/settings?term=$module";
2836 + $default_url = self::admin_url( array( 'page' => 'jetpack-settings' ) ) . "#/settings?term=$module";
2726 2837 /**
2727 2838 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
2728 2839 *
2729 2840 * @since 6.9.0
@@ -2741,9 +2852,9 @@
2741 2852 *
2742 2853 * @param string $message Error message.
2743 2854 * @param bool $deactivate Deactivate Jetpack or not.
2744 2855 *
2745 - * @return void
2856 + * @return never
2746 2857 */
2747 2858 public static function bail_on_activation( $message, $deactivate = true ) {
2748 2859 ?>
2749 2860 <!doctype html>
@@ -2781,9 +2892,9 @@
2781 2892 if ( $update ) {
2782 2893 update_option( 'active_plugins', array_filter( $plugins ) );
2783 2894 }
2784 2895 }
2785 - exit;
2896 + exit( 0 );
2786 2897 }
2787 2898
2788 2899 /**
2789 2900 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
@@ -2808,12 +2919,18 @@
2808 2919 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
2809 2920
2810 2921 Health::on_jetpack_activated();
2811 2922
2923 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
2924 +
2812 2925 if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) {
2813 2926 Sync_Actions::do_only_first_initial_sync();
2814 2927 }
2815 2928
2929 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
2930 + Woocommerce_Analytics::maybe_add_proxy_speed_module();
2931 + }
2932 +
2816 2933 self::plugin_initialize();
2817 2934 }
2818 2935
2819 2936 /**
@@ -2848,9 +2965,9 @@
2848 2965
2849 2966 if ( $plugins_path === $referer['path'] ) {
2850 2967 $source_type = 'list';
2851 2968 } elseif ( $plugins_install_path === $referer['path'] ) {
2852 - $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
2969 + $tab = $query_parts['tab'] ?? 'featured';
2853 2970 switch ( $tab ) {
2854 2971 case 'popular':
2855 2972 $source_type = 'popular';
2856 2973 break;
@@ -2860,10 +2977,10 @@
2860 2977 case 'favorites':
2861 2978 $source_type = 'favorites';
2862 2979 break;
2863 2980 case 'search':
2864 - $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
2865 - $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
2981 + $source_type = 'search-' . ( $query_parts['type'] ?? 'term' );
2982 + $source_query = $query_parts['s'] ?? null;
2866 2983 break;
2867 2984 default:
2868 2985 $source_type = 'featured';
2869 2986 }
@@ -2872,29 +2989,171 @@
2872 2989 return array( $source_type, $source_query );
2873 2990 }
2874 2991
2875 2992 /**
2876 - * Runs before bumping version numbers up to a new version
2993 + * Runs before bumping version numbers up to a new version.
2877 2994 *
2878 - * @param string $version Version:timestamp.
2995 + * Only ever registered the hooks for the release post update modal, which has been removed.
2996 + * No longer hooked to `updating_jetpack_version`.
2997 + *
2998 + * @deprecated 16.2
2999 + *
3000 + * @param string $version Version:timestamp.
2879 3001 * @param string $old_version Old Version:timestamp or false if not set yet.
2880 3002 */
2881 - public static function do_version_bump( $version, $old_version ) {
2882 - if ( $old_version ) { // For existing Jetpack installations.
2883 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3003 + public static function do_version_bump( $version, $old_version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Signature preserved for the deprecation shim.
3004 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
3005 + }
2884 3006
2885 - // If a front end page is visited after the update, the 'wp' action will fire.
2886 - add_action( 'wp', 'Jetpack::set_update_modal_display' );
3007 + /**
3008 + * Enables the Newsletter (subscriptions) module for existing sites now that it is a default-on module.
3009 + *
3010 + * Fresh installs receive the module via its "Auto Activate: Yes" header, so this only handles sites
3011 + * upgrading from a version where the module defaulted off. It runs once per site (guarded by the
3012 + * subscriptions_default_on_migrated option). Fresh installs are marked as migrated immediately so the
3013 + * migration never runs for them. After it has run, the user's choice to deactivate the module again
3014 + * (for example from the My Jetpack Products page) is respected and never reverted.
3015 + *
3016 + * The module requires a connection, so on a disconnected site the migration is deferred without setting
3017 + * the guard, allowing a later version bump to retry once the site is connected.
3018 + *
3019 + * @param string $version New Jetpack version:timestamp.
3020 + * @param string|false $old_version Previous Jetpack version:timestamp, or false on a fresh install.
3021 + */
3022 + public static function activate_subscriptions_module_for_existing_sites( $version, $old_version ) {
3023 + if ( get_option( 'jetpack_subscriptions_default_on_migrated' ) ) {
3024 + return;
3025 + }
2887 3026
2888 - // If an admin page is visited after the update, the 'current_screen' action will fire.
2889 - add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3027 + // Fresh installs get the module via its "Auto Activate: Yes" header. Mark them as migrated so a
3028 + // later opt-out is never reverted by the existing-site path on a subsequent version bump.
3029 + if ( ! $old_version ) {
3030 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3031 + return;
2890 3032 }
3033 +
3034 + if ( ! self::is_connection_ready() ) {
3035 + return;
3036 + }
3037 +
3038 + // Mark as migrated only once the module is active, so a transient activation failure is retried on
3039 + // a later version bump rather than being silently skipped.
3040 + if ( self::is_module_active( 'subscriptions' ) || self::activate_module( 'subscriptions', false, false ) ) {
3041 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3042 + }
2891 3043 }
2892 3044
2893 3045 /**
3046 + * Option flag that records the AI master-switch opt-out reconciliation has run,
3047 + * so it never runs twice.
3048 + *
3049 + * @var string
3050 + */
3051 + const AI_MASTER_OPTOUT_MIGRATED_OPTION = 'jetpack_ai_master_optout_migrated';
3052 +
3053 + /**
3054 + * Register the on-upgrade init hooks whose relative ORDER matters, extracted so
3055 + * the ordering can be asserted in tests without invoking plugin_upgrade() (whose
3056 + * guards make it unreliable to trigger under test). activate_new_modules()
3057 + * (init, default priority 10) auto-activates "Auto Activate: Yes" modules
3058 + * including the `ai` master; reconcile_ai_master_optout() must run at a LATER
3059 + * priority to honor an explicit AI opt-out.
3060 + *
3061 + * @return void
3062 + */
3063 + public static function register_upgrade_init_hooks() {
3064 + add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
3065 + add_action( 'init', array( __CLASS__, 'reconcile_ai_master_optout' ), 20 );
3066 + }
3067 +
3068 + /**
3069 + * Preserves an explicit Jetpack AI opt-out when the `ai` module becomes the site-wide
3070 + * master switch off WordPress.com Simple (self-hosted and Atomic).
3071 + *
3072 + * The `ai` module is "Auto Activate: Yes", so on upgrade {@see self::activate_new_modules()}
3073 + * turns it on for connected sites — the desired default-on / auto-enable-on-connection
3074 + * behavior, which this method deliberately leaves alone. The one case it corrects is a site
3075 + * that had explicitly disabled Jetpack AI (the `jetpack_ai_enabled` option present and falsey)
3076 + * before the module shipped: that opt-out must survive the module becoming the master, so the
3077 + * module is deactivated for exactly those sites. An absent or truthy option is left untouched.
3078 + *
3079 + * Ordering is the whole point. `activate_new_modules()` is hooked on `init` at priority 10 and
3080 + * auto-activates the module there; this method is hooked on `init` at priority 20 (see
3081 + * {@see self::plugin_upgrade()}), so it runs AFTER the auto-activation and its deactivation is
3082 + * the final state. A version-guarded block that ran inline during `plugins_loaded` would be
3083 + * undone by the later auto-activation, which is why this is a late-init hook rather than an
3084 + * inline upgrade step.
3085 + *
3086 + * WordPress.com Simple never runs modules — the option stays the master there — so this is a
3087 + * no-op on Simple. The {@see self::AI_MASTER_OPTOUT_MIGRATED_OPTION} flag makes it run exactly
3088 + * once, which matters because off-Simple the option is no longer the master after this runs:
3089 + * a stale falsey option must not keep re-deactivating a module the user later turns back on.
3090 + *
3091 + * @return void
3092 + */
3093 + public static function reconcile_ai_master_optout() {
3094 + if ( get_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION ) ) {
3095 + return;
3096 + }
3097 +
3098 + // Simple keeps the `jetpack_ai_enabled` option as the master; modules don't run there.
3099 + if ( ( new Host() )->is_wpcom_simple() ) {
3100 + return;
3101 + }
3102 +
3103 + // A sentinel default distinguishes an absent option (leave auto-activation alone) from one
3104 + // explicitly stored falsey (an opt-out to preserve).
3105 + $stored = get_option( 'jetpack_ai_enabled', 'not-set' );
3106 + if ( 'not-set' !== $stored && ! (bool) $stored ) {
3107 + ( new Modules() )->deactivate( 'ai' );
3108 + }
3109 +
3110 + update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3111 + }
3112 +
3113 + /**
3114 + * Deletes obsolete SEO module-state options without changing module activation.
3115 + *
3116 + * @since 16.3
3117 + */
3118 + public static function cleanup_seo_module_state_options() {
3119 + delete_option( 'jetpack_seo_sitemap_enabled' );
3120 + delete_option( 'jetpack_seo_canonical_urls_enabled' );
3121 + delete_option( 'jetpack_seo_module_state_reconciled' );
3122 + }
3123 +
3124 + /**
3125 + * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3126 + * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3127 + *
3128 + * Hooked on `updating_jetpack_version`, which fires on every install including the
3129 + * first — with `$old_version === false` on a brand-new site (the same signal
3130 + * {@see self::activate_subscriptions_module_for_existing_sites()} keys off). Fresh
3131 + * installs are seeded visible; existing installs are seeded hidden and opt in later
3132 + * via the legacy Traffic page or My Jetpack. `add_option()` makes this seed-once: it
3133 + * never overrides a value a later opt-in (or opt-out) has set. WordPress.com sites
3134 + * ignore this option entirely (always visible) — see
3135 + * {@see \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()}.
3136 + *
3137 + * @param string $version The new Jetpack version (unused).
3138 + * @param string|false $old_version The previous version, or false on a fresh install.
3139 + */
3140 + public static function seed_seo_visibility_cohort( $version, $old_version ) {
3141 + add_option( Jetpack_SEO_Initializer::VISIBILITY_OPTION, ! $old_version );
3142 + }
3143 +
3144 + /**
2894 3145 * Sets the display_update_modal state.
3146 + *
3147 + * The release post update modal that read this state has been removed. The write is kept so the
3148 + * method still behaves as documented for the deprecation window. When this is deleted, also drop
3149 + * the matching `display_update_modal` guard in Automattic\Jetpack\CookieState::should_set_cookie(),
3150 + * which exists only to keep this key out of the cookie on the Jetpack admin screen.
3151 + *
3152 + * @deprecated 16.2
2895 3153 */
2896 3154 public static function set_update_modal_display() {
3155 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2897 3156 self::state( 'display_update_modal', true );
2898 3157 }
2899 3158
2900 3159 /**
@@ -2899,11 +3158,16 @@
2899 3158
2900 3159 /**
2901 3160 * Enqueues the block library styles.
2902 3161 *
3162 + * Only ever used by the release post update modal, which has been removed.
3163 + *
3164 + * @deprecated 16.2
3165 + *
2903 3166 * @param string $hook The current admin page.
2904 3167 */
2905 3168 public static function enqueue_block_style( $hook ) {
3169 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2906 3170 if ( 'toplevel_page_jetpack' === $hook ) {
2907 3171 wp_enqueue_style( 'wp-block-library' );
2908 3172 }
2909 3173 }
@@ -2932,9 +3196,8 @@
2932 3196
2933 3197 self::load_modules();
2934 3198
2935 3199 Jetpack_Options::delete_option( 'do_activate' );
2936 - Jetpack_Options::delete_option( 'dismissed_connection_banner' );
2937 3200 }
2938 3201
2939 3202 /**
2940 3203 * Handles the activation of the default modules depending on the current state of the site:
@@ -2993,8 +3256,12 @@
2993 3256 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
2994 3257 self::disconnect();
2995 3258 Jetpack_Options::delete_option( 'version' );
2996 3259 }
3260 +
3261 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
3262 + Woocommerce_Analytics::maybe_remove_proxy_speed_module();
3263 + }
2997 3264 }
2998 3265
2999 3266 /**
3000 3267 * Set activated option to 4 on jetpack_idc_disconnect action.
@@ -3022,9 +3289,9 @@
3022 3289 $connection->remove_connection( ! Identity_Crisis::validate_sync_error_idc_option() );
3023 3290 }
3024 3291
3025 3292 /**
3026 - * Happens after a successfull disconnection.
3293 + * Happens after a successful disconnection.
3027 3294 *
3028 3295 * @static
3029 3296 */
3030 3297 public static function jetpack_site_disconnected() {
@@ -3029,8 +3296,10 @@
3029 3296 */
3030 3297 public static function jetpack_site_disconnected() {
3031 3298 Identity_Crisis::clear_all_idc_options();
3032 3299
3300 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
3301 +
3033 3302 // Delete all the sync related data. Since it could be taking up space.
3034 3303 Sender::get_instance()->uninstall();
3035 3304
3036 3305 /**
@@ -3047,10 +3316,13 @@
3047 3316 }
3048 3317 }
3049 3318
3050 3319 /**
3051 - * Disconnects the user
3320 + * Disconnects the user.
3052 3321 *
3322 + * @deprecated 13.4
3323 + * @see \Automattic\Jetpack\Connection\Manager::disconnect_user()
3324 + *
3053 3325 * @param int $user_id The user ID to disconnect.
3054 3326 */
3055 3327 public function disconnect_user( $user_id ) {
3056 3328 $this->connection_manager->disconnect_user( $user_id );
@@ -3056,21 +3328,8 @@
3056 3328 $this->connection_manager->disconnect_user( $user_id );
3057 3329 }
3058 3330
3059 3331 /**
3060 - * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3061 - *
3062 - * @deprecated since Jetpack 9.7.0
3063 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
3064 - *
3065 - * @return bool|WP_Error
3066 - */
3067 - public static function try_registration() {
3068 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3069 - return static::connection()->try_registration();
3070 - }
3071 -
3072 - /**
3073 3332 * Checking the domain names in beta versions.
3074 3333 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3075 3334 *
3076 3335 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
@@ -3077,9 +3336,9 @@
3077 3336 *
3078 3337 * @return null|\WP_Error The domain validation error, or `null` if everything's fine.
3079 3338 */
3080 3339 public static function registration_check_domains( $error ) {
3081 - if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3340 + if ( static::is_development_version() ) {
3082 3341 $domains_to_check = array_unique(
3083 3342 array(
3084 3343 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3085 3344 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
@@ -3104,10 +3363,17 @@
3104 3363 * @param mixed $code Error code to log.
3105 3364 * @param mixed $data Data to log.
3106 3365 */
3107 3366 public static function log( $code, $data = null ) {
3367 +
3368 + $raw_log = Jetpack_Options::get_option( 'log', array() );
3369 + // This can be modified by the `jetpack_options` filter, so abort if we don't have an array.
3370 + if ( ! is_array( $raw_log ) ) {
3371 + return;
3372 + }
3373 +
3108 3374 // only grab the latest 200 entries.
3109 - $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3375 + $log = array_slice( $raw_log, -199, 199 );
3110 3376
3111 3377 // Append our event to the log.
3112 3378 $log_entry = array(
3113 3379 'time' => time(),
@@ -3207,8 +3473,15 @@
3207 3473
3208 3474 /**
3209 3475 * Return stat data for WPCOM sync.
3210 3476 *
3477 + * The Sync stats module was this method's last caller and moved to the Connection package's
3478 + * `Heartbeat::generate_stats_array()`, which assembles the heartbeat data through the
3479 + * `jetpack_heartbeat_stats_array` filter. Note the package method does not include the extended
3480 + * data from `get_additional_stat_data()`, so callers relying on `$extended` need to add it themselves.
3481 + *
3482 + * @deprecated 16.2
3483 + *
3211 3484 * @param bool $encode JSON encode the result.
3212 3485 * @param bool $extended Adds additional stats data.
3213 3486 *
3214 3487 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
@@ -3213,10 +3486,15 @@
3213 3486 *
3214 3487 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
3215 3488 */
3216 3489 public static function get_stat_data( $encode = true, $extended = true ) {
3217 - $data = Jetpack_Heartbeat::generate_stats_array();
3490 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3218 3491
3492 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
3493 + ? Heartbeat::get_environment_stats()
3494 + : array();
3495 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), $env_stats );
3496 +
3219 3497 if ( $extended ) {
3220 3498 $additional_data = self::get_additional_stat_data();
3221 3499 $data = array_merge( $data, $additional_data );
3222 3500 }
@@ -3221,9 +3499,9 @@
3221 3499 $data = array_merge( $data, $additional_data );
3222 3500 }
3223 3501
3224 3502 if ( $encode ) {
3225 - return wp_json_encode( $data );
3503 + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
3226 3504 }
3227 3505
3228 3506 return $data;
3229 3507 }
@@ -3295,23 +3573,24 @@
3295 3573 $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
3296 3574 /** Already documented in automattic/jetpack-connection::src/class-client.php */
3297 3575 $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false );
3298 3576
3299 - if ( ! $is_offline_mode ) {
3300 - Jetpack_Connection_Banner::init();
3301 - Jetpack_Connection_Widget::init();
3302 - }
3577 + // Run post-activation actions if needed.
3578 + $this->plugin_post_activation();
3303 3579
3304 3580 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3305 3581 // Upgrade: 1.1 -> 1.1.1
3306 3582 // Check and see if host can verify the Jetpack servers' SSL certificate.
3307 3583 $args = array();
3584 + // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
3308 3585 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3309 3586 }
3310 3587
3311 - Jetpack_Recommendations_Banner::init();
3312 -
3313 - if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3588 + if (
3589 + current_user_can( 'manage_options' )
3590 + && ! self::permit_ssl()
3591 + && ! $is_offline_mode
3592 + ) {
3314 3593 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3315 3594 }
3316 3595
3317 3596 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
@@ -3320,12 +3599,8 @@
3320 3599 if ( ! ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) && ! is_network_admin() ) ) {
3321 3600 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3322 3601 }
3323 3602
3324 - if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3325 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3326 - }
3327 -
3328 3603 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3329 3604
3330 3605 if ( self::is_connection_ready() || $is_offline_mode ) {
3331 3606 // Artificially throw errors in certain specific cases during plugin activation.
@@ -3330,13 +3605,8 @@
3330 3605 if ( self::is_connection_ready() || $is_offline_mode ) {
3331 3606 // Artificially throw errors in certain specific cases during plugin activation.
3332 3607 add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3333 3608 }
3334 -
3335 - // Add custom column in wp-admin/users.php to show whether user is linked.
3336 - add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3337 - add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3338 - add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3339 3609 }
3340 3610
3341 3611 /**
3342 3612 * Adds body classes.
@@ -3369,8 +3639,9 @@
3369 3639 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3370 3640 * This function artificially throws errors for such cases (per a specific list).
3371 3641 *
3372 3642 * @param string $plugin The activated plugin.
3643 + * @throws RuntimeException If a conflicting plugin is detected.
3373 3644 */
3374 3645 public function throw_error_on_activate_plugin( $plugin ) {
3375 3646 $active_modules = self::get_active_modules();
3376 3647
@@ -3383,8 +3654,9 @@
3383 3654 if ( 'stats.php' === basename( $plugin ) ) {
3384 3655 $throw = true;
3385 3656 }
3386 3657 } else {
3658 + // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Checked above. See also https://github.com/phan/phan/issues/1204.
3387 3659 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3388 3660 if ( basename( $plugin ) === basename( $reflection->getFileName() ) ) {
3389 3661 $throw = true;
3390 3662 }
@@ -3391,9 +3663,9 @@
3391 3663 }
3392 3664
3393 3665 if ( $throw ) {
3394 3666 /* translators: Plugin name to deactivate. */
3395 - 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
3667 + throw new RuntimeException( sprintf( __( 'Jetpack contains the most recent version of the old "%1$s" plugin.', 'jetpack' ), 'WordPress.com Stats' ) );
3396 3668 }
3397 3669 }
3398 3670 }
3399 3671
@@ -3475,8 +3747,9 @@
3475 3747 /**
3476 3748 * Handler for Jetpack remote file uploads.
3477 3749 *
3478 3750 * @access public
3751 + * @return never
3479 3752 */
3480 3753 public function remote_request_handlers() {
3481 3754 switch ( current_filter() ) {
3482 3755 case 'wp_ajax_nopriv_jetpack_upload_file':
@@ -3503,18 +3776,17 @@
3503 3776 if ( ! is_int( $status_code ) ) {
3504 3777 $status_code = 400;
3505 3778 }
3506 3779
3507 - status_header( $status_code );
3508 - die( wp_json_encode( (object) compact( 'error', 'error_description' ) ) );
3780 + wp_send_json( (object) compact( 'error', 'error_description' ), $status_code, JSON_UNESCAPED_SLASHES );
3509 3781 }
3510 3782
3511 - status_header( 200 );
3512 3783 if ( true === $response ) {
3513 - exit;
3784 + status_header( 200 );
3785 + exit( 0 );
3514 3786 }
3515 3787
3516 - die( wp_json_encode( (object) $response ) );
3788 + wp_send_json( (object) $response, 200, JSON_UNESCAPED_SLASHES );
3517 3789 }
3518 3790
3519 3791 /**
3520 3792 * Uploads a file gotten from the global $_FILES.
@@ -3522,9 +3794,9 @@
3522 3794 * the attachment file of the media item (gotten through of the post_id)
3523 3795 * will be updated instead of add a new one.
3524 3796 *
3525 3797 * @param boolean $update_media_item - update media attachment.
3526 - * @return array - An array describing the uploadind files process.
3798 + * @return array|WP_Error - An array describing the uploading files process.
3527 3799 */
3528 3800 public function upload_handler( $update_media_item = false ) {
3529 3801 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
3530 3802 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
@@ -3575,9 +3847,9 @@
3575 3847 return new WP_Error( 'handler_cannot_upload', __( 'The upload handler cannot upload files', 'jetpack' ), 400 );
3576 3848 }
3577 3849
3578 3850 $uploaded_files = array();
3579 - $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3851 + $global_post = $GLOBALS['post'] ?? null;
3580 3852 unset( $GLOBALS['post'] );
3581 3853 if ( empty( $_FILES['media']['name'] ) ) {
3582 3854 // Nothing to process, just return.
3583 3855 return $uploaded_files;
@@ -3584,9 +3856,9 @@
3584 3856 }
3585 3857 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.
3586 3858 $file = array();
3587 3859 foreach ( $media_keys as $media_key ) {
3588 - $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.
3860 + $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.
3589 3861 }
3590 3862
3591 3863 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.
3592 3864
@@ -3632,9 +3904,9 @@
3632 3904 'type' => (string) $edited_media_item->post_mime_type,
3633 3905 'meta' => (array) wp_get_attachment_metadata( $post_id ),
3634 3906 );
3635 3907
3636 - return (array) array( $response );
3908 + return array( $response );
3637 3909 }
3638 3910
3639 3911 $attachment_id = media_handle_upload(
3640 3912 '.jetpack.upload.',
@@ -3673,67 +3945,8 @@
3673 3945 return $uploaded_files;
3674 3946 }
3675 3947
3676 3948 /**
3677 - * Add help to the Jetpack page
3678 - *
3679 - * @since Jetpack (1.2.3)
3680 - * @return void
3681 - */
3682 - public function admin_help() {
3683 - $current_screen = get_current_screen();
3684 -
3685 - // Overview.
3686 - $current_screen->add_help_tab(
3687 - array(
3688 - 'id' => 'home',
3689 - 'title' => __( 'Home', 'jetpack' ),
3690 - 'content' =>
3691 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3692 - '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3693 - '<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>',
3694 - )
3695 - );
3696 -
3697 - // Screen Content.
3698 - if ( current_user_can( 'manage_options' ) ) {
3699 - $current_screen->add_help_tab(
3700 - array(
3701 - 'id' => 'settings',
3702 - 'title' => __( 'Settings', 'jetpack' ),
3703 - 'content' =>
3704 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3705 - '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3706 - '<ol>' .
3707 - '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3708 - '<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>' .
3709 - '</ol>' .
3710 - '<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>',
3711 - )
3712 - );
3713 - }
3714 -
3715 - // Help Sidebar.
3716 - $support_url = Redirect::get_url( 'jetpack-support' );
3717 - $faq_url = Redirect::get_url( 'jetpack-faq' );
3718 - $current_screen->set_help_sidebar(
3719 - '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3720 - '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3721 - '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3722 - '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3723 - );
3724 - }
3725 -
3726 - /**
3727 - * Enqueues the jetpack-icons style.
3728 - *
3729 - * @return void
3730 - */
3731 - public function admin_menu_css() {
3732 - wp_enqueue_style( 'jetpack-icons' );
3733 - }
3734 -
3735 - /**
3736 3949 * Add action links for the Jetpack plugin.
3737 3950 *
3738 3951 * @param array $actions Plugin actions.
3739 3952 *
@@ -3739,14 +3952,11 @@
3739 3952 *
3740 3953 * @return array
3741 3954 */
3742 3955 public function plugin_action_links( $actions ) {
3743 - $support_link = ( new Host() )->is_woa_site() ? 'https://wordpress.com/help/contact/' : self::admin_url( 'page=jetpack-debugger' );
3744 -
3745 3956 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
3746 3957 return array_merge(
3747 - array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3748 - array( 'support' => sprintf( '<a href="%s">%s</a>', $support_link, __( 'Support', 'jetpack' ) ) ),
3958 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack-settings#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3749 3959 $actions
3750 3960 );
3751 3961 }
3752 3962
@@ -3753,53 +3963,8 @@
3753 3963 return $actions;
3754 3964 }
3755 3965
3756 3966 /**
3757 - * Add an activation modal to the plugins page and the main dashboard.
3758 - *
3759 - * @param string $hook The current admin page.
3760 - *
3761 - * @return void
3762 - */
3763 - public function activate_dialog( $hook ) {
3764 - /*
3765 - * We do not need it on other plugin pages,
3766 - * or when Jetpack is already connected.
3767 - */
3768 - if (
3769 - ! in_array( $hook, array( 'plugins.php', 'index.php' ), true )
3770 - || self::is_connection_ready()
3771 - ) {
3772 - return;
3773 - }
3774 -
3775 - // add an activation script that will pick up deactivation actions for the Jetpack plugin.
3776 - Assets::register_script(
3777 - 'jetpack-full-activation-modal-js',
3778 - '_inc/build/activation-modal.js',
3779 - JETPACK__PLUGIN_FILE,
3780 - array(
3781 - 'enqueue' => true,
3782 - 'in_footer' => true,
3783 - 'textdomain' => 'jetpack',
3784 - 'dependencies' => array(
3785 - 'wp-polyfill',
3786 - 'wp-components',
3787 - ),
3788 - )
3789 - );
3790 -
3791 - // Add objects to be passed to the initial state of the app.
3792 - // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3793 - 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' );
3794 -
3795 - // Adds Connection package initial state.
3796 - wp_add_inline_script( 'jetpack-full-activation-modal-js', Connection_Initial_State::render(), 'before' );
3797 -
3798 - add_action( 'admin_notices', array( $this, 'jetpack_plugin_portal_containers' ) );
3799 - }
3800 -
3801 - /**
3802 3967 * Adds the deactivation warning modal for Jetpack.
3803 3968 *
3804 3969 * @param string $hook The current admin page.
3805 3970 *
@@ -3819,14 +3984,10 @@
3819 3984 'jetpack-plugins-page-js',
3820 3985 '_inc/build/plugins-page.js',
3821 3986 JETPACK__PLUGIN_FILE,
3822 3987 array(
3823 - 'in_footer' => true,
3824 - 'textdomain' => 'jetpack',
3825 - 'dependencies' => array(
3826 - 'wp-polyfill',
3827 - 'wp-components',
3828 - ),
3988 + 'in_footer' => true,
3989 + 'textdomain' => 'jetpack',
3829 3990 )
3830 3991 );
3831 3992 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3832 3993
@@ -3831,9 +3992,9 @@
3831 3992 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3832 3993
3833 3994 // Add objects to be passed to the initial state of the app.
3834 3995 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3835 - 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' );
3996 + 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' );
3836 3997
3837 3998 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
3838 3999 }
3839 4000 }
@@ -3882,9 +4043,9 @@
3882 4043 // @todo provide way to go to specific calypso env.
3883 4044 self::get_calypso_host() . 'jetpack/connect'
3884 4045 )
3885 4046 );
3886 - exit;
4047 + exit( 0 );
3887 4048 }
3888 4049 }
3889 4050
3890 4051 /*
@@ -3919,8 +4080,28 @@
3919 4080 * Done!
3920 4081 */
3921 4082
3922 4083 /**
4084 + * Build the user-facing description stored alongside a registration error code.
4085 + *
4086 + * @since 16.2
4087 + *
4088 + * @param string $error_code The WP_Error code.
4089 + * @param string $message The WP_Error message.
4090 + * @return string The description, empty when the message is not user-facing copy.
4091 + */
4092 + public static function get_registration_error_description( $error_code, $message ) {
4093 + // Manager::validate_remote_register_response() does not always put user-facing copy in the
4094 + // message slot: wpcom_5??, wpcom_408 and wpcom_bad_response store the HTTP status there,
4095 + // and jetpack_id stores the raw response body, which can also overflow the state cookie.
4096 + if ( 'jetpack_id' === $error_code || is_numeric( $message ) ) {
4097 + return '';
4098 + }
4099 +
4100 + return mb_substr( (string) $message, 0, 250 );
4101 + }
4102 +
4103 + /**
3923 4104 * Handles the page load events for the Jetpack admin page
3924 4105 */
3925 4106 public function admin_page_load() {
3926 4107 $error = false;
@@ -3956,10 +4137,11 @@
3956 4137 $registered = static::connection()->try_registration();
3957 4138 if ( is_wp_error( $registered ) ) {
3958 4139 $error = $registered->get_error_code();
3959 4140 self::state( 'error', $error );
3960 - self::state( 'error', $registered->get_error_message() );
3961 4141
4142 + self::state( 'error_description', self::get_registration_error_description( $error, $registered->get_error_message() ) );
4143 +
3962 4144 /**
3963 4145 * Jetpack registration Error.
3964 4146 *
3965 4147 * @since 7.5.0
@@ -3983,12 +4165,8 @@
3983 4165 do_action( 'jetpack_connection_register_success', $from );
3984 4166
3985 4167 $url = $this->build_connect_url( true, $redirect, $from );
3986 4168
3987 - if ( ! empty( $_GET['onboarding'] ) ) {
3988 - $url = add_query_arg( 'onboarding', rawurlencode_deep( wp_unslash( $_GET['onboarding'] ) ), $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3989 - }
3990 -
3991 4169 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
3992 4170 $url = add_query_arg( 'auth_approved', 'true', $url );
3993 4171 }
3994 4172
@@ -3993,9 +4171,9 @@
3993 4171 }
3994 4172
3995 4173 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3996 4174 wp_safe_redirect( $url );
3997 - exit;
4175 + exit( 0 );
3998 4176 case 'activate':
3999 4177 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4000 4178 $error = 'cheatin';
4001 4179 break;
@@ -4009,9 +4187,9 @@
4009 4187 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4010 4188 }
4011 4189 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4012 4190 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4013 - exit;
4191 + exit( 0 );
4014 4192 case 'activate_default_modules':
4015 4193 check_admin_referer( 'activate_default_modules' );
4016 4194 self::log( 'activate_default_modules' );
4017 4195 self::restate();
@@ -4019,9 +4197,9 @@
4019 4197 $max_version = isset( $_GET['max_version'] ) ? sanitize_text_field( wp_unslash( $_GET['max_version'] ) ) : false;
4020 4198 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_GET['other_modules'] ) ) : array();
4021 4199 self::activate_default_modules( $min_version, $max_version, $other_modules );
4022 4200 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4023 - exit;
4201 + exit( 0 );
4024 4202 case 'disconnect':
4025 4203 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4026 4204 $error = 'cheatin';
4027 4205 break;
@@ -4030,9 +4208,9 @@
4030 4208 check_admin_referer( 'jetpack-disconnect' );
4031 4209 self::log( 'disconnect' );
4032 4210 self::disconnect();
4033 4211 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4034 - exit;
4212 + exit( 0 );
4035 4213 case 'reconnect':
4036 4214 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4037 4215 $error = 'cheatin';
4038 4216 break;
@@ -4043,9 +4221,9 @@
4043 4221 self::disconnect();
4044 4222
4045 4223 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4046 4224 wp_safe_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4047 - exit;
4225 + exit( 0 );
4048 4226 case 'deactivate':
4049 4227 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4050 4228 $error = 'cheatin';
4051 4229 break;
@@ -4059,9 +4237,9 @@
4059 4237 self::state( 'message', 'module_deactivated' );
4060 4238 }
4061 4239 self::state( 'module', $modules );
4062 4240 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4063 - exit;
4241 + exit( 0 );
4064 4242 case 'unlink':
4065 4243 $redirect = isset( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : '';
4066 4244 check_admin_referer( 'jetpack-unlink' );
4067 4245 self::log( 'unlink' );
@@ -4071,32 +4249,9 @@
4071 4249 wp_safe_redirect( admin_url() );
4072 4250 } else {
4073 4251 wp_safe_redirect( self::admin_url( array( 'page' => rawurlencode( $redirect ) ) ) );
4074 4252 }
4075 - exit;
4076 - case 'onboard':
4077 - if ( ! current_user_can( 'manage_options' ) ) {
4078 - wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4079 - } else {
4080 - self::create_onboarding_token();
4081 - $url = $this->build_connect_url( true );
4082 -
4083 - $token = Jetpack_Options::get_option( 'onboarding' );
4084 -
4085 - if ( false !== ( $token ) ) {
4086 - $url = add_query_arg( 'onboarding', $token, $url );
4087 - }
4088 -
4089 - $calypso_env = $this->get_calypso_env();
4090 - if ( ! empty( $calypso_env ) ) {
4091 - $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4092 - }
4093 -
4094 - add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4095 - wp_safe_redirect( $url );
4096 - exit;
4097 - }
4098 - exit;
4253 + exit( 0 );
4099 4254 default:
4100 4255 /**
4101 4256 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4102 4257 *
@@ -4112,9 +4267,10 @@
4112 4267 if ( ! $error ) {
4113 4268 self::activate_new_modules( true );
4114 4269 }
4115 4270
4116 - $message_code = self::state( 'message' );
4271 + $activated_manage = false;
4272 + $message_code = self::state( 'message' );
4117 4273 if ( self::state( 'optin-manage' ) ) {
4118 4274 $activated_manage = $message_code;
4119 4275 $message_code = 'jetpack-manage';
4120 4276 }
@@ -4288,17 +4444,19 @@
4288 4444 count( $privacy_checks ),
4289 4445 '%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4290 4446 'jetpack'
4291 4447 ),
4292 - wp_nonce_url(
4293 - self::admin_url(
4294 - array(
4295 - 'page' => 'jetpack',
4296 - 'action' => 'deactivate',
4297 - 'module' => rawurlencode( $module_slugs ),
4298 - )
4299 - ),
4300 - "jetpack_deactivate-$module_slugs"
4448 + esc_url(
4449 + wp_nonce_url(
4450 + self::admin_url(
4451 + array(
4452 + 'page' => 'jetpack',
4453 + 'action' => 'deactivate',
4454 + 'module' => rawurlencode( $module_slugs ),
4455 + )
4456 + ),
4457 + "jetpack_deactivate-$module_slugs"
4458 + )
4301 4459 ),
4302 4460 esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4303 4461 ),
4304 4462 array(
@@ -4316,37 +4474,8 @@
4316 4474 endif;
4317 4475 }
4318 4476
4319 4477 /**
4320 - * We can't always respond to a signed XML-RPC request with a
4321 - * helpful error message. In some circumstances, doing so could
4322 - * leak information.
4323 - *
4324 - * Instead, track that the error occurred via a Jetpack_Option,
4325 - * and send that data back in the heartbeat.
4326 - * All this does is increment a number, but it's enough to find
4327 - * trends.
4328 - *
4329 - * @param WP_Error $xmlrpc_error The error produced during
4330 - * signature validation.
4331 - */
4332 - public function track_xmlrpc_error( $xmlrpc_error ) {
4333 - $code = is_wp_error( $xmlrpc_error )
4334 - ? $xmlrpc_error->get_error_code()
4335 - : 'should-not-happen';
4336 -
4337 - $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4338 - if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4339 - // No need to update the option if we already have
4340 - // this code stored.
4341 - return;
4342 - }
4343 - $xmlrpc_errors[ $code ] = true;
4344 -
4345 - Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4346 - }
4347 -
4348 - /**
4349 4478 * Initialize the jetpack stats instance only when needed
4350 4479 *
4351 4480 * @return void
4352 4481 */
@@ -4444,9 +4573,9 @@
4444 4573 if ( is_network_admin() ) {
4445 4574 $url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4446 4575 }
4447 4576
4448 - $calypso_env = self::get_calypso_env();
4577 + $calypso_env = ( new Host() )->get_calypso_env();
4449 4578
4450 4579 if ( ! empty( $calypso_env ) ) {
4451 4580 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4452 4581 }
@@ -4469,9 +4598,9 @@
4469 4598 return $this->build_connect_url( $raw, $redirect, $from, true );
4470 4599 }
4471 4600 }
4472 4601
4473 - $url = $this->build_authorize_url( $redirect );
4602 + $url = ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4474 4603 }
4475 4604
4476 4605 if ( $from ) {
4477 4606 $url = add_query_arg( 'from', $from, $url );
@@ -4496,66 +4625,30 @@
4496 4625 * @param null $deprecated Deprecated since Jetpack 10.9.
4497 4626 *
4498 4627 * @todo Update default value for redirect since the called function expects a string.
4499 4628 *
4629 + * @deprecated 13.4
4630 + *
4500 4631 * @return mixed|void
4501 4632 */
4502 4633 public static function build_authorize_url( $redirect = false, $deprecated = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
4634 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::build_authorize_url' );
4503 4635
4504 - add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4505 - add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4506 -
4507 - $c8n = self::connection();
4508 - $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4509 -
4510 - remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4511 - remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4512 -
4513 - /**
4514 - * Filter the URL used when authorizing a user to a WordPress.com account.
4515 - *
4516 - * @since 8.9.0
4517 - *
4518 - * @param string $url Connection URL.
4519 - */
4520 - return apply_filters( 'jetpack_build_authorize_url', $url );
4636 + return ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4521 4637 }
4522 4638
4523 4639 /**
4524 4640 * Filters the connection URL parameter array.
4525 4641 *
4642 + * @deprecated 13.4
4643 + *
4526 4644 * @param array $args default URL parameters used by the package.
4527 4645 * @return array the modified URL arguments array.
4528 4646 */
4529 4647 public static function filter_connect_request_body( $args ) {
4530 - if (
4531 - Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4532 - && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4533 - ) {
4534 - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4535 - $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4536 - ? $gp_locale->slug
4537 - : '';
4538 - }
4648 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_request_body' );
4539 4649
4540 - $tracking = new Tracking();
4541 - $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4542 -
4543 - $args = array_merge(
4544 - $args,
4545 - array(
4546 - '_ui' => $tracks_identity['_ui'],
4547 - '_ut' => $tracks_identity['_ut'],
4548 - )
4549 - );
4550 -
4551 - $calypso_env = self::get_calypso_env();
4552 -
4553 - if ( ! empty( $calypso_env ) ) {
4554 - $args['calypso_env'] = $calypso_env;
4555 - }
4556 -
4557 - return $args;
4650 + return Authorize_Redirect::filter_connect_request_body( $args );
4558 4651 }
4559 4652
4560 4653 /**
4561 4654 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
@@ -4592,41 +4685,19 @@
4592 4685 return $current_user_connection_data;
4593 4686 }
4594 4687
4595 4688 /**
4596 - * Filters the URL that will process the connection data. It can be different from the URL
4597 - * that we send the user to after everything is done.
4598 - *
4599 - * @param String $processing_url the default redirect URL used by the package.
4600 - * @return String the modified URL.
4601 - *
4602 - * @deprecated since Jetpack 9.5.0
4603 - */
4604 - public static function filter_connect_processing_url( $processing_url ) {
4605 - _deprecated_function( __METHOD__, 'jetpack-9.5' );
4606 -
4607 - $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4608 - return $processing_url;
4609 - }
4610 -
4611 - /**
4612 4689 * Filters the redirection URL that is used for connect requests. The redirect
4613 4690 * URL should return the user back to the Jetpack console.
4614 4691 *
4692 + * @deprecated 13.4
4693 + *
4615 4694 * @param String $redirect the default redirect URL used by the package.
4616 4695 * @return String the modified URL.
4617 4696 */
4618 4697 public static function filter_connect_redirect_url( $redirect ) {
4619 - $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4620 - $redirect = $redirect
4621 - ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4622 - : $jetpack_admin_page;
4623 -
4624 - if ( isset( $_REQUEST['is_multisite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- not making a site change here.
4625 - $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4626 - }
4627 -
4628 - return $redirect;
4698 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_redirect_url' );
4699 + return Authorize_Redirect::filter_connect_redirect_url( $redirect );
4629 4700 }
4630 4701
4631 4702 /**
4632 4703 * This action fires at the beginning of the Manager::authorize method.
@@ -4690,11 +4761,8 @@
4690 4761 *
4691 4762 * @param array $data The request data.
4692 4763 */
4693 4764 public static function authorize_ending_authorized( $data ) {
4694 - // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4695 - self::invalidate_onboarding_token();
4696 -
4697 4765 // If redirect_uri is SSO, ensure SSO module is enabled.
4698 4766 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4699 4767
4700 4768 /** This filter is documented in class.jetpack-cli.php */
@@ -4818,13 +4886,9 @@
4818 4886 *
4819 4887 * @return int 0 if the same sort or (+/-) to indicate which is greater.
4820 4888 */
4821 4889 public static function sort_modules( $a, $b ) {
4822 - if ( $a['sort'] === $b['sort'] ) {
4823 - return 0;
4824 - }
4825 -
4826 - return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4890 + return $a['sort'] <=> $b['sort'];
4827 4891 }
4828 4892
4829 4893 /**
4830 4894 * Recheck SSL status for use via an AJAX call.
@@ -4838,10 +4902,12 @@
4838 4902 $result = self::permit_ssl( true );
4839 4903 wp_send_json(
4840 4904 array(
4841 4905 'enabled' => $result,
4842 - 'message' => get_transient( 'jetpack_https_test_message' ),
4843 - )
4906 + 'message' => self::get_ssl_test_message(),
4907 + ),
4908 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
4909 + JSON_UNESCAPED_SLASHES
4844 4910 );
4845 4911 }
4846 4912
4847 4913 /* Client API */
@@ -4848,8 +4914,10 @@
4848 4914
4849 4915 /**
4850 4916 * Verify the onboarding token.
4851 4917 *
4918 + * @deprecated since 13.9
4919 + *
4852 4920 * @param array $token_data Token data.
4853 4921 * @param string $token Token value.
4854 4922 * @param string $request_data JSON-encoded request data.
4855 4923 *
@@ -4855,8 +4923,9 @@
4855 4923 *
4856 4924 * @return mixed
4857 4925 */
4858 4926 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
4927 + _deprecated_function( __METHOD__, '13.9' );
4859 4928 // Default to a blog token.
4860 4929 $token_type = 'blog';
4861 4930
4862 4931 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
@@ -4884,9 +4953,9 @@
4884 4953 $jp_user = get_user_by( 'email', $jpo_user );
4885 4954 if ( is_a( $jp_user, 'WP_User' ) ) {
4886 4955 wp_set_current_user( $jp_user->ID );
4887 4956 $user_can = is_multisite()
4888 - ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
4957 + ? current_user_can_for_site( get_current_blog_id(), 'manage_options' )
4889 4958 : current_user_can( 'manage_options' );
4890 4959 if ( $user_can ) {
4891 4960 $token_type = 'user';
4892 4961 $token->external_user_id = $jp_user->ID;
@@ -4903,11 +4972,13 @@
4903 4972
4904 4973 /**
4905 4974 * Create a random secret for validating onboarding payload
4906 4975 *
4976 + * @deprecated since 13.9
4907 4977 * @return string Secret token
4908 4978 */
4909 4979 public static function create_onboarding_token() {
4980 + _deprecated_function( __METHOD__, '13.9' );
4910 4981 $token = Jetpack_Options::get_option( 'onboarding' );
4911 4982 if ( false === ( $token ) ) {
4912 4983 $token = wp_generate_password( 32, false );
4913 4984 Jetpack_Options::update_option( 'onboarding', $token );
@@ -4918,11 +4989,13 @@
4918 4989
4919 4990 /**
4920 4991 * Remove the onboarding token
4921 4992 *
4993 + * @deprecated since 13.9
4922 4994 * @return bool True on success, false on failure
4923 4995 */
4924 4996 public static function invalidate_onboarding_token() {
4997 + _deprecated_function( __METHOD__, '13.9' );
4925 4998 return Jetpack_Options::delete_option( 'onboarding' );
4926 4999 }
4927 5000
4928 5001 /**
@@ -4927,8 +5000,10 @@
4927 5000
4928 5001 /**
4929 5002 * Validate an onboarding token for a specific action
4930 5003 *
5004 + * @deprecated since 13.9
5005 + *
4931 5006 * @param string $token Onboarding token.
4932 5007 * @param string $action Action name.
4933 5008 *
4934 5009 * @return boolean True if token/action pair is accepted, false if not
@@ -4933,8 +5008,9 @@
4933 5008 *
4934 5009 * @return boolean True if token/action pair is accepted, false if not
4935 5010 */
4936 5011 public static function validate_onboarding_token_action( $token, $action ) {
5012 + _deprecated_function( __METHOD__, '13.9' );
4937 5013 // Compare tokens, bail if tokens do not match.
4938 5014 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4939 5015 return false;
4940 5016 }
@@ -4960,39 +5036,41 @@
4960 5036 * @return boolean
4961 5037 * @since 2.3.3
4962 5038 */
4963 5039 public static function permit_ssl( $force_recheck = false ) {
4964 - // Do some fancy tests to see if ssl is being supported.
4965 - if ( ! $force_recheck ) {
4966 - $ssl = get_transient( 'jetpack_https_test' );
5040 + if ( ! method_exists( Heartbeat::class, 'permit_ssl' ) ) {
5041 + // Skip the SSL-fail notice when the check cannot run.
5042 + return true;
4967 5043 }
4968 5044
4969 - if ( $force_recheck || false === $ssl ) {
4970 - $message = '';
4971 - if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4972 - $ssl = 0;
4973 - } else {
4974 - $ssl = 1;
5045 + return Heartbeat::permit_ssl( $force_recheck );
5046 + }
4975 5047
4976 - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4977 - $ssl = 0;
4978 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4979 - } else {
4980 - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4981 - if ( is_wp_error( $response ) ) {
4982 - $ssl = 0;
4983 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4984 - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4985 - $ssl = 0;
4986 - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4987 - }
4988 - }
4989 - }
4990 - set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4991 - set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5048 + /**
5049 + * Returns a localized message describing the last SSL connectivity failure, if any.
5050 + *
5051 + * The Connection package's canonical SSL check stores a neutral reason code; this maps it to
5052 + * a translated, `jetpack`-domain message for display in the admin notice and AJAX recheck.
5053 + *
5054 + * @since 16.1
5055 + *
5056 + * @return string The localized message, or an empty string when there is no failure.
5057 + */
5058 + public static function get_ssl_test_message() {
5059 + if ( ! method_exists( Heartbeat::class, 'get_ssl_test_error' ) ) {
5060 + return '';
4992 5061 }
4993 5062
4994 - return (bool) $ssl;
5063 + $error = Heartbeat::get_ssl_test_error();
5064 +
5065 + switch ( $error['code'] ) {
5066 + case 'no_ssl_support':
5067 + return __( 'WordPress reports no SSL support', 'jetpack' );
5068 + case 'bad_response':
5069 + return __( 'Response was not OK: ', 'jetpack' ) . $error['detail'];
5070 + default:
5071 + return '';
5072 + }
4995 5073 }
4996 5074
4997 5075 /**
4998 5076 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
@@ -5011,9 +5089,9 @@
5011 5089 <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>
5012 5090 <p>
5013 5091 <?php esc_html_e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5014 5092 <a href="#" id="jetpack-recheck-ssl-button"><?php esc_html_e( 'Try again', 'jetpack' ); ?></a>
5015 - <span id="jetpack-recheck-ssl-output"><?php echo esc_html( get_transient( 'jetpack_https_test_message' ) ); ?></span>
5093 + <span id="jetpack-recheck-ssl-output"><?php echo esc_html( self::get_ssl_test_message() ); ?></span>
5016 5094 </p>
5017 5095 <p>
5018 5096 <?php
5019 5097 printf(
@@ -5032,18 +5110,18 @@
5032 5110 <script type="text/javascript">
5033 5111 jQuery( document ).ready( function( $ ) {
5034 5112 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5035 5113 var $this = $( this );
5036 - $this.html( <?php echo wp_json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5114 + $this.html( <?php echo wp_json_encode( esc_html__( 'Checking', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5037 5115 $( '#jetpack-recheck-ssl-output' ).html( '' );
5038 5116 e.preventDefault();
5039 - var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce ); ?> };
5117 + var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> };
5040 5118 $.post( ajaxurl, data )
5041 5119 .done( function( response ) {
5042 5120 if ( response.enabled ) {
5043 5121 $( '#jetpack-ssl-warning' ).hide();
5044 5122 } else {
5045 - this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5123 + this.html( <?php echo wp_json_encode( esc_html__( 'Try again', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5046 5124 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5047 5125 }
5048 5126 }.bind( $this ) );
5049 5127 } );
@@ -5069,26 +5147,8 @@
5069 5147 return $jetpack->connection_manager;
5070 5148 }
5071 5149
5072 5150 /**
5073 - * Creates two secret tokens and the end of life timestamp for them.
5074 - *
5075 - * Note these tokens are unique per call, NOT static per site for connecting.
5076 - *
5077 - * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5078 - *
5079 - * @since 2.6
5080 - * @param String $action The action name.
5081 - * @param Integer $user_id The user identifier.
5082 - * @param Integer $exp Expiration time in seconds.
5083 - * @return array
5084 - */
5085 - public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5086 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5087 - return self::connection()->generate_secrets( $action, $user_id, $exp );
5088 - }
5089 -
5090 - /**
5091 5151 * Get verification secrets.
5092 5152 *
5093 5153 * @param string $action Action name.
5094 5154 * @param int $user_id User ID.
@@ -5109,35 +5169,8 @@
5109 5169 return $secrets;
5110 5170 }
5111 5171
5112 5172 /**
5113 - * Register a connection.
5114 - *
5115 - * @deprecated Jetpack 9.7.0
5116 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
5117 - *
5118 - * @return bool|WP_Error
5119 - */
5120 - public static function register() {
5121 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5122 - return static::connection()->try_registration( false );
5123 - }
5124 -
5125 - /**
5126 - * Filters the registration request body to include tracking properties.
5127 - *
5128 - * @deprecated Jetpack 9.7.0
5129 - * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5130 - *
5131 - * @param array $properties Token request properties.
5132 - * @return array amended properties.
5133 - */
5134 - public static function filter_register_request_body( $properties ) {
5135 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5136 - return Connection_Utils::filter_register_request_body( $properties );
5137 - }
5138 -
5139 - /**
5140 5173 * Filters the token request body to include tracking properties.
5141 5174 *
5142 5175 * @param array $properties Token request properties.
5143 5176 *
@@ -5158,9 +5191,9 @@
5158 5191
5159 5192 /**
5160 5193 * If the db version is showing something other that what we've got now, bump it to current.
5161 5194 *
5162 - * @return bool: True if the option was incorrect and updated, false if nothing happened.
5195 + * @return bool True if the option was incorrect and updated, false if nothing happened.
5163 5196 */
5164 5197 public static function maybe_set_version_option() {
5165 5198 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5166 5199 if ( JETPACK__VERSION !== $version ) {
@@ -5178,21 +5211,8 @@
5178 5211
5179 5212 /* Client Server API */
5180 5213
5181 5214 /**
5182 - * Loads the Jetpack XML-RPC client.
5183 - * No longer necessary, as the XML-RPC client will be automagically loaded.
5184 - *
5185 - * Note: we cannot remove this function yet as it is used in this plugin:
5186 - * https://wordpress.org/plugins/jetpack-subscription-form/
5187 - *
5188 - * @deprecated since 7.7.0
5189 - */
5190 - public static function load_xml_rpc_client() {
5191 - _deprecated_function( __METHOD__, 'jetpack-7.7' );
5192 - }
5193 -
5194 - /**
5195 5215 * State is passed via cookies from one request to the next, but never to subsequent requests.
5196 5216 * SET: state( $key, $value );
5197 5217 * GET: $value = state( $key );
5198 5218 *
@@ -5266,20 +5286,8 @@
5266 5286
5267 5287 self::state( 'privacy_checks', $privacy_checks );
5268 5288 }
5269 5289
5270 - /**
5271 - * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5272 - *
5273 - * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5274 - *
5275 - * @param string $url WordPress.com static resource URL.
5276 - */
5277 - public static function staticize_subdomain( $url ) {
5278 - _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5279 - return Assets::staticize_subdomain( $url );
5280 - }
5281 -
5282 5290 /* JSON API Authorization */
5283 5291
5284 5292 /**
5285 5293 * Handles the login action for Authorizing the JSON API
@@ -5284,13 +5292,14 @@
5284 5292 /**
5285 5293 * Handles the login action for Authorizing the JSON API
5286 5294 */
5287 5295 public function login_form_json_api_authorization() {
5288 - $this->verify_json_api_authorization_request();
5296 + $authorize_json_api = new Authorize_Json_Api();
5297 + $authorize_json_api->verify_json_api_authorization_request();
5289 5298
5290 - add_action( 'wp_login', array( $this, 'store_json_api_authorization_token' ), 10, 2 );
5299 + add_action( 'wp_login', array( $authorize_json_api, 'store_json_api_authorization_token' ), 10, 2 );
5291 5300
5292 - add_action( 'login_message', array( $this, 'login_message_json_api_authorization' ) );
5301 + add_action( 'login_message', array( $authorize_json_api, 'login_message_json_api_authorization' ) );
5293 5302 add_action( 'login_form', array( $this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5294 5303 add_filter( 'site_url', array( $this, 'post_login_form_to_signed_url' ), 10, 3 );
5295 5304 }
5296 5305
@@ -5328,16 +5337,17 @@
5328 5337
5329 5338 /**
5330 5339 * If someone logs in to approve API access, store the Access Code in usermeta.
5331 5340 *
5341 + * @deprecated 13.4
5342 + *
5332 5343 * @param string $user_login Unused.
5333 5344 * @param WP_User $user User logged in.
5334 5345 */
5335 5346 public function store_json_api_authorization_token( $user_login, $user ) {
5336 - add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5337 - add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_public_api_domain' ) );
5338 - $token = wp_generate_password( 32, false );
5339 - update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5347 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::store_json_api_authorization_token' );
5348 +
5349 + return ( new Authorize_Json_Api() )->store_json_api_authorization_token( $user_login, $user );
5340 5350 }
5341 5351
5342 5352 /**
5343 5353 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
@@ -5343,23 +5353,29 @@
5343 5353 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5344 5354 *
5345 5355 * To be used with a filter of allowed domains for a redirect.
5346 5356 *
5357 + * @deprecated 13.4
5358 + *
5347 5359 * @param array $domains Allowed WP.com Environments.
5348 5360 */
5349 5361 public function allow_wpcom_public_api_domain( $domains ) {
5350 - $domains[] = 'public-api.wordpress.com';
5351 - return $domains;
5362 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Status\\Host::allow_wpcom_public_api_domain' );
5363 +
5364 + return Host::allow_wpcom_public_api_domain( $domains );
5352 5365 }
5353 5366
5354 5367 /**
5355 5368 * Check if the redirect is encoded.
5356 5369 *
5370 + * @deprecated 13.4
5371 + *
5357 5372 * @param string $redirect_url Redirect URL.
5358 5373 *
5359 5374 * @return bool If redirect has been encoded.
5360 5375 */
5361 5376 public static function is_redirect_encoded( $redirect_url ) {
5377 + _deprecated_function( __METHOD__, 'jetpack-13.4' );
5362 5378 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5363 5379 }
5364 5380
5365 5381 /**
@@ -5377,8 +5393,10 @@
5377 5393
5378 5394 /**
5379 5395 * Add the Access Code details to the public-api.wordpress.com redirect.
5380 5396 *
5397 + * @deprecated 13.4
5398 + *
5381 5399 * @param string $redirect_to URL.
5382 5400 * @param string $original_redirect_to URL.
5383 5401 * @param WP_User $user WP_User for the redirect.
5384 5402 *
@@ -5384,23 +5402,18 @@
5384 5402 *
5385 5403 * @return string
5386 5404 */
5387 5405 public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5388 - return add_query_arg(
5389 - urlencode_deep(
5390 - array(
5391 - 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5392 - 'jetpack-user-id' => (int) $user->ID,
5393 - 'jetpack-state' => $this->json_api_authorization_request['state'],
5394 - )
5395 - ),
5396 - $redirect_to
5397 - );
5406 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::add_token_to_login_redirect_json_api_authorization' );
5407 +
5408 + return ( new Authorize_Json_Api() )->add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user );
5398 5409 }
5399 5410
5400 5411 /**
5401 5412 * Verifies the request by checking the signature
5402 5413 *
5414 + * @deprecated 13.4
5415 + *
5403 5416 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5404 5417 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5405 5418 *
5406 5419 * @param null|array $environment Value to override $_REQUEST.
@@ -5405,194 +5418,24 @@
5405 5418 *
5406 5419 * @param null|array $environment Value to override $_REQUEST.
5407 5420 */
5408 5421 public function verify_json_api_authorization_request( $environment = null ) {
5409 - $environment = $environment === null
5410 - ? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function.
5411 - : $environment;
5422 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::verify_json_api_authorization_request' );
5412 5423
5413 - list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
5414 - $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
5415 - if ( ! $token || empty( $token->secret ) ) {
5416 - wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5417 - }
5418 -
5419 - $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' );
5420 -
5421 - // Host has encoded the request URL, probably as a result of a bad http => https redirect.
5422 - 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.
5423 - /**
5424 - * Jetpack authorisation request Error.
5425 - *
5426 - * @since 7.5.0
5427 - */
5428 - do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5429 - $die_error = sprintf(
5430 - /* translators: %s is a URL */
5431 - __( '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' ),
5432 - Redirect::get_url( 'jetpack-support-double-encoding' )
5433 - );
5434 - }
5435 -
5436 - $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5437 -
5438 - if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5439 - $signature = $jetpack_signature->sign_request(
5440 - $environment['token'],
5441 - $environment['timestamp'],
5442 - $environment['nonce'],
5443 - '',
5444 - 'GET',
5445 - $environment['jetpack_json_api_original_query'],
5446 - null,
5447 - true
5448 - );
5449 - } else {
5450 - $signature = $jetpack_signature->sign_current_request(
5451 - array(
5452 - 'body' => null,
5453 - 'method' => 'GET',
5454 - )
5455 - );
5456 - }
5457 -
5458 - if ( ! $signature ) {
5459 - wp_die(
5460 - wp_kses(
5461 - $die_error,
5462 - array(
5463 - 'a' => array(
5464 - 'href' => array(),
5465 - ),
5466 - )
5467 - )
5468 - );
5469 - } elseif ( is_wp_error( $signature ) ) {
5470 - wp_die(
5471 - wp_kses(
5472 - $die_error,
5473 - array(
5474 - 'a' => array(
5475 - 'href' => array(),
5476 - ),
5477 - )
5478 - )
5479 - );
5480 - } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5481 - if ( is_ssl() ) {
5482 - // 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.
5483 - $signature = $jetpack_signature->sign_current_request(
5484 - array(
5485 - 'scheme' => 'http',
5486 - 'body' => null,
5487 - 'method' => 'GET',
5488 - )
5489 - );
5490 - if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5491 - wp_die(
5492 - wp_kses(
5493 - $die_error,
5494 - array(
5495 - 'a' => array(
5496 - 'href' => array(),
5497 - ),
5498 - )
5499 - )
5500 - );
5501 - }
5502 - } else {
5503 - wp_die(
5504 - wp_kses(
5505 - $die_error,
5506 - array(
5507 - 'a' => array(
5508 - 'href' => array(),
5509 - ),
5510 - )
5511 - )
5512 - );
5513 - }
5514 - }
5515 -
5516 - $timestamp = (int) $environment['timestamp'];
5517 - $nonce = stripslashes( (string) $environment['nonce'] );
5518 -
5519 - if ( ! $this->connection_manager ) {
5520 - $this->connection_manager = new Connection_Manager();
5521 - }
5522 -
5523 - if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
5524 - // De-nonce the nonce, at least for 5 minutes.
5525 - // 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).
5526 - $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5527 - if ( $old_nonce_time < time() - 300 ) {
5528 - wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
5529 - }
5530 - }
5531 -
5532 - $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
5533 - $data_filters = array(
5534 - 'state' => 'opaque',
5535 - 'client_id' => 'int',
5536 - 'client_title' => 'string',
5537 - 'client_image' => 'url',
5538 - );
5539 -
5540 - foreach ( $data_filters as $key => $sanitation ) {
5541 - if ( ! isset( $data->$key ) ) {
5542 - wp_die(
5543 - wp_kses(
5544 - $die_error,
5545 - array(
5546 - 'a' => array(
5547 - 'href' => array(),
5548 - ),
5549 - )
5550 - )
5551 - );
5552 - }
5553 -
5554 - switch ( $sanitation ) {
5555 - case 'int':
5556 - $this->json_api_authorization_request[ $key ] = (int) $data->$key;
5557 - break;
5558 - case 'opaque':
5559 - $this->json_api_authorization_request[ $key ] = (string) $data->$key;
5560 - break;
5561 - case 'string':
5562 - $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
5563 - break;
5564 - case 'url':
5565 - $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
5566 - break;
5567 - }
5568 - }
5569 -
5570 - if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5571 - wp_die(
5572 - wp_kses(
5573 - $die_error,
5574 - array(
5575 - 'a' => array(
5576 - 'href' => array(),
5577 - ),
5578 - )
5579 - )
5580 - );
5581 - }
5424 + return ( new Authorize_Json_Api() )->verify_json_api_authorization_request( $environment );
5582 5425 }
5583 5426
5584 5427 /**
5585 5428 * HTML for the JSON API authorization notice.
5586 5429 *
5430 + * @deprecated 13.4
5431 + *
5587 5432 * @return string
5588 5433 */
5589 5434 public function login_message_json_api_authorization() {
5590 - return '<p class="message">' . sprintf(
5591 - /* translators: Name/image of the client requesting authorization */
5592 - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
5593 - '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5594 - ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5435 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::login_message_json_api_authorization' );
5436 +
5437 + return ( new Authorize_Json_Api() )->login_message_json_api_authorization();
5595 5438 }
5596 5439
5597 5440 /**
5598 5441 * Get $content_width, but with a <s>twist</s> filter.
@@ -5636,32 +5479,23 @@
5636 5479
5637 5480 /**
5638 5481 * Checks if the site is currently in an identity crisis.
5639 5482 *
5483 + * Now delegates to the Connection package so this matches what the heartbeat itself reports.
5484 + * Note the package guards on `Connection\Manager::is_connected()` where this used to guard on
5485 + * `Jetpack::is_connection_ready()`, so the `jetpack_is_connection_ready` filter no longer applies.
5486 + *
5487 + * @deprecated 16.2
5488 + *
5640 5489 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
5641 5490 */
5642 5491 public static function check_identity_crisis() {
5643 - if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
5644 - return false;
5645 - }
5492 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Identity_Crisis::check_identity_crisis' );
5646 5493
5647 - return Jetpack_Options::get_option( 'sync_error_idc' );
5494 + return Identity_Crisis::check_identity_crisis();
5648 5495 }
5649 5496
5650 5497 /**
5651 - * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
5652 - *
5653 - * @since 4.4.0
5654 - * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
5655 - *
5656 - * @return bool
5657 - */
5658 - public static function validate_sync_error_idc_option() {
5659 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' );
5660 - return Identity_Crisis::validate_sync_error_idc_option();
5661 - }
5662 -
5663 - /**
5664 5498 * Normalizes a url by doing three things:
5665 5499 * - Strips protocol
5666 5500 * - Strips www
5667 5501 * - Adds a trailing slash
@@ -5682,35 +5516,8 @@
5682 5516 return $url;
5683 5517 }
5684 5518
5685 5519 /**
5686 - * Gets the value that is to be saved in the jetpack_sync_error_idc option.
5687 - *
5688 - * @since 4.4.0
5689 - * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
5690 - * @deprecated 9.8.0 Use \\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option
5691 - *
5692 - * @param array $response HTTP response.
5693 - * @return array Array of the local urls, wpcom urls, and error code
5694 - */
5695 - public static function get_sync_error_idc_option( $response = array() ) {
5696 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' );
5697 - return Identity_Crisis::get_sync_error_idc_option( $response );
5698 - }
5699 -
5700 - /**
5701 - * Returns the value of the jetpack_sync_idc_optin filter, or constant.
5702 - * If set to true, the site will be put into staging mode.
5703 - *
5704 - * @since 4.3.2
5705 - * @return bool
5706 - */
5707 - public static function sync_idc_optin() {
5708 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' );
5709 - return Identity_Crisis::sync_idc_optin();
5710 - }
5711 -
5712 - /**
5713 5520 * Maybe Use a .min.css stylesheet, maybe not.
5714 5521 *
5715 5522 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
5716 5523 *
@@ -5731,9 +5538,9 @@
5731 5538 // Strip out the abspath.
5732 5539 $base = dirname( plugin_basename( $plugin ) );
5733 5540
5734 5541 // Short out on non-Jetpack assets.
5735 - if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
5542 + if ( ! str_starts_with( $base, 'jetpack/' ) ) {
5736 5543 return $url;
5737 5544 }
5738 5545
5739 5546 // File name parsing.
@@ -5773,15 +5580,15 @@
5773 5580 *
5774 5581 * @return mixed
5775 5582 */
5776 5583 public static function set_suffix_on_min( $src, $handle ) {
5777 - if ( false === strpos( $src, '.min.css' ) ) {
5584 + if ( ! is_string( $src ) || ! str_contains( $src, '.min.css' ) ) {
5778 5585 return $src;
5779 5586 }
5780 5587
5781 5588 if ( ! empty( self::$min_assets ) ) {
5782 5589 foreach ( self::$min_assets as $file => $path ) {
5783 - if ( false !== strpos( $src, $file ) ) {
5590 + if ( str_contains( $src, $file ) ) {
5784 5591 wp_style_add_data( $handle, 'suffix', '.min' );
5785 5592 return $src;
5786 5593 }
5787 5594 }
@@ -5811,8 +5618,10 @@
5811 5618 *
5812 5619 * Data passed in with the $data parameter will be available in the
5813 5620 * template file as $data['value']
5814 5621 *
5622 + * @html-template-var array $data
5623 + *
5815 5624 * @param string $template - Template file to load.
5816 5625 * @param array $data - Any data to pass along to the template.
5817 5626 * @return boolean - If template file was found.
5818 5627 **/
@@ -5830,8 +5639,19 @@
5830 5639 return false;
5831 5640 }
5832 5641
5833 5642 /**
5643 + * Register Jetpack-specific tests on the connection package's health test suite.
5644 + *
5645 + * @param \Automattic\Jetpack\Connection\Connection_Health_Tests $connection_tests The test suite instance.
5646 + */
5647 + public function register_jetpack_connection_tests( $connection_tests ) {
5648 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger/class-jetpack-cxn-tests.php';
5649 + $jetpack_tests = new Jetpack_Cxn_Tests();
5650 + $jetpack_tests->register_tests_on( $connection_tests );
5651 + }
5652 +
5653 + /**
5834 5654 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
5835 5655 */
5836 5656 public function deprecated_hooks() {
5837 5657 $filter_deprecated_list = array(
@@ -5958,13 +5778,8 @@
5958 5778 'jetpack_cache_plans' => array(
5959 5779 'replacement' => null,
5960 5780 'version' => 'jetpack-6.1.0',
5961 5781 ),
5962 -
5963 - 'jetpack_lazy_images_skip_image_with_atttributes' => array(
5964 - 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
5965 - 'version' => 'jetpack-6.5.0',
5966 - ),
5967 5782 'jetpack_enable_site_verification' => array(
5968 5783 'replacement' => null,
5969 5784 'version' => 'jetpack-6.5.0',
5970 5785 ),
@@ -6048,8 +5863,22 @@
6048 5863 'jetpack_are_blogging_prompts_enabled' => array(
6049 5864 'replacement' => null,
6050 5865 'version' => 'jetpack-11.8.0',
6051 5866 ),
5867 + 'jetpack_subscriptions_modal_enabled' => array(
5868 + 'replacement' => null,
5869 + 'version' => 'jetpack-12.7.0',
5870 + ),
5871 + 'jetpack_pre_connection_prompt_helpers' => array(
5872 + 'replacement' => null,
5873 + 'version' => 'jetpack-13.2.0',
5874 + ),
5875 + 'jetpack_contact_form_use_package' => array(
5876 + 'replacement' => null,
5877 + 'version' => 'jetpack-13.4.0',
5878 + ),
5879 + // jetpack_implode_frontend_css has been removed, but is not listed here. The updated behavior is exactly the only use of the filter.
5880 + // We can reassess formally deprecating it here later; for now, it would be noise with no functional difference.
6052 5881 );
6053 5882
6054 5883 foreach ( $filter_deprecated_list as $tag => $args ) {
6055 5884 if ( has_filter( $tag ) ) {
@@ -6152,9 +5981,9 @@
6152 5981 foreach ( $matches as $match ) {
6153 5982 $url = trim( $match['path'], "'\" \t" );
6154 5983
6155 5984 // If this is a data url, we don't want to mess with it.
6156 - if ( 'data:' === substr( $url, 0, 5 ) ) {
5985 + if ( str_starts_with( $url, 'data:' ) ) {
6157 5986 continue;
6158 5987 }
6159 5988
6160 5989 // If this is an absolute or protocol-agnostic url,
@@ -6180,113 +6009,8 @@
6180 6009 return $css;
6181 6010 }
6182 6011
6183 6012 /**
6184 - * This methods removes all of the registered css files on the front end
6185 - * from Jetpack in favor of using a single file. In effect "imploding"
6186 - * all the files into one file.
6187 - *
6188 - * Pros:
6189 - * - Uses only ONE css asset connection instead of 15
6190 - * - Saves a minimum of 56k
6191 - * - Reduces server load
6192 - * - Reduces time to first painted byte
6193 - *
6194 - * Cons:
6195 - * - Loads css for ALL modules. However all selectors are prefixed so it
6196 - * should not cause any issues with themes.
6197 - * - Plugins/themes dequeuing styles no longer do anything. See
6198 - * jetpack_implode_frontend_css filter for a workaround
6199 - *
6200 - * For some situations developers may wish to disable css imploding and
6201 - * instead operate in legacy mode where each file loads seperately and
6202 - * can be edited individually or dequeued. This can be accomplished with
6203 - * the following line:
6204 - *
6205 - * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6206 - *
6207 - * @param bool $travis_test Is this a test run.
6208 - *
6209 - * @since 3.2
6210 - */
6211 - public function implode_frontend_css( $travis_test = false ) {
6212 - $do_implode = true;
6213 - if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6214 - $do_implode = false;
6215 - }
6216 -
6217 - // Do not implode CSS when the page loads via the AMP plugin.
6218 - if ( class_exists( Jetpack_AMP_Support::class ) && Jetpack_AMP_Support::is_amp_request() ) {
6219 - $do_implode = false;
6220 - }
6221 -
6222 - /**
6223 - * Allow CSS to be concatenated into a single jetpack.css file.
6224 - *
6225 - * @since 3.2.0
6226 - *
6227 - * @param bool $do_implode Should CSS be concatenated? Default to true.
6228 - */
6229 - $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6230 -
6231 - // Do not use the imploded file when default behavior was altered through the filter.
6232 - if ( ! $do_implode ) {
6233 - return;
6234 - }
6235 -
6236 - // We do not want to use the imploded file in dev mode, or if not connected.
6237 - if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6238 - if ( ! $travis_test ) {
6239 - return;
6240 - }
6241 - }
6242 -
6243 - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen.
6244 - if ( get_option( 'sharedaddy_disable_resources' ) ) {
6245 - return;
6246 - }
6247 -
6248 - /*
6249 - * Now we assume Jetpack is connected and able to serve the single
6250 - * file.
6251 - *
6252 - * In the future there will be a check here to serve the file locally
6253 - * or potentially from the Jetpack CDN
6254 - *
6255 - * For now:
6256 - * - Enqueue a single imploded css file
6257 - * - Zero out the style_loader_tag for the bundled ones
6258 - * - Be happy, drink scotch
6259 - */
6260 -
6261 - add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6262 -
6263 - $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6264 -
6265 - wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6266 - wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6267 - }
6268 -
6269 - /**
6270 - * Removes styles that are part of concatenated group.
6271 - *
6272 - * @param string $tag Style tag.
6273 - * @param string $handle Style handle.
6274 - *
6275 - * @return string
6276 - */
6277 - public function concat_remove_style_loader_tag( $tag, $handle ) {
6278 - if ( in_array( $handle, $this->concatenated_style_handles, true ) ) {
6279 - $tag = '';
6280 - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6281 - $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6282 - }
6283 - }
6284 -
6285 - return $tag;
6286 - }
6287 -
6288 - /**
6289 6013 * Check the heartbeat data
6290 6014 *
6291 6015 * Organizes the heartbeat data by severity. For example, if the site
6292 6016 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
@@ -6298,9 +6022,23 @@
6298 6022 *
6299 6023 * $return array $filtered_data
6300 6024 */
6301 6025 public static function jetpack_check_heartbeat_data() {
6302 - $raw_data = Jetpack_Heartbeat::generate_stats_array();
6026 + /*
6027 + * Site environment stats (incl. wp-version/php-version checked below) now live in the Connection package,
6028 + * and the IDC stat is contributed by the Connection package's `jetpack_heartbeat_stats_array` filter
6029 + * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6030 + * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6031 + * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6032 + */
6033 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
6034 + ? Heartbeat::get_environment_stats()
6035 + : array();
6036 + $raw_data = array_merge(
6037 + Jetpack_Heartbeat::generate_stats_array(),
6038 + $env_stats,
6039 + array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6040 + );
6303 6041
6304 6042 $good = array();
6305 6043 $caution = array();
6306 6044 $bad = array();
@@ -6366,23 +6104,8 @@
6366 6104 return Jetpack_Options::get_options_for_reset();
6367 6105 }
6368 6106
6369 6107 /**
6370 - * Strip http:// or https:// from a url, replaces forward slash with ::,
6371 - * so we can bring them directly to their site in calypso.
6372 - *
6373 - * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6374 - *
6375 - * @param string $url URL.
6376 - * @return string url without the guff.
6377 - */
6378 - public static function build_raw_urls( $url ) {
6379 - _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6380 -
6381 - return ( new Status() )->get_site_suffix( $url );
6382 - }
6383 -
6384 - /**
6385 6108 * Get the user's meta box order.
6386 6109 *
6387 6110 * @param array $sorted Value for the user's option.
6388 6111 * @return mixed
@@ -6392,9 +6115,9 @@
6392 6115 return $sorted;
6393 6116 }
6394 6117
6395 6118 foreach ( $sorted as $box_context => $ids ) {
6396 - if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6119 + if ( ! str_contains( $ids, 'dashboard_stats' ) ) {
6397 6120 // If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6398 6121 continue;
6399 6122 }
6400 6123
@@ -6411,68 +6134,9 @@
6411 6134 }
6412 6135
6413 6136 return $sorted;
6414 6137 }
6415 -
6416 6138 /**
6417 - * Adds a "blank" column in the user admin table to display indication of user connection.
6418 - *
6419 - * @param array $columns User list table columns.
6420 - *
6421 - * @return array
6422 - */
6423 - public function jetpack_icon_user_connected( $columns ) {
6424 - $columns['user_jetpack'] = '';
6425 - return $columns;
6426 - }
6427 -
6428 - /**
6429 - * Show Jetpack icon if the user is linked.
6430 - *
6431 - * @param string $val HTML for the icon.
6432 - * @param string $col User list table column.
6433 - * @param int $user_id User ID.
6434 - *
6435 - * @return string
6436 - */
6437 - public function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
6438 - if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) {
6439 - $jetpack_logo = new Jetpack_Logo();
6440 - $emblem_html = sprintf(
6441 - '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
6442 - esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
6443 - $jetpack_logo->get_jp_emblem()
6444 - );
6445 - return $emblem_html;
6446 - }
6447 -
6448 - return $val;
6449 - }
6450 -
6451 - /**
6452 - * Style the Jetpack user column
6453 - */
6454 - public function jetpack_user_col_style() {
6455 - global $current_screen;
6456 - if ( ! empty( $current_screen->base ) && 'users' === $current_screen->base ) {
6457 - ?>
6458 - <style>
6459 - .fixed .column-user_jetpack {
6460 - width: 21px;
6461 - }
6462 - .jp-emblem-user-admin svg {
6463 - width: 20px;
6464 - height: 20px;
6465 - }
6466 - .jp-emblem-user-admin path {
6467 - fill: #069e08;
6468 - }
6469 - </style>
6470 - <?php
6471 - }
6472 - }
6473 -
6474 - /**
6475 6139 * Checks if Akismet is active and working.
6476 6140 *
6477 6141 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
6478 6142 * that implied usage of methods present since more recent version.
@@ -6564,26 +6228,15 @@
6564 6228 /**
6565 6229 * Return Calypso environment value; used for developing Jetpack and pairing
6566 6230 * it with different Calypso enrionments, such as localhost.
6567 6231 *
6568 - * @since 7.4.0
6232 + * @deprecated 12.4 Moved to the Status package.
6569 6233 *
6570 6234 * @return string Calypso environment
6571 6235 */
6572 6236 public static function get_calypso_env() {
6573 - if ( isset( $_GET['calypso_env'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6574 - return sanitize_key( $_GET['calypso_env'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6575 - }
6576 -
6577 - if ( getenv( 'CALYPSO_ENV' ) ) {
6578 - return sanitize_key( getenv( 'CALYPSO_ENV' ) );
6579 - }
6580 -
6581 - if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
6582 - return sanitize_key( CALYPSO_ENV );
6583 - }
6584 -
6585 - return '';
6237 + _deprecated_function( __METHOD__, 'jetpack-12.4', '\Automattic\Jetpack\Status\Host->get_calypso_env' );
6238 + return ( new Host() )->get_calypso_env();
6586 6239 }
6587 6240
6588 6241 /**
6589 6242 * Returns the hostname with protocol for Calypso.
@@ -6593,9 +6246,9 @@
6593 6246 *
6594 6247 * @return string Calypso host.
6595 6248 */
6596 6249 public static function get_calypso_host() {
6597 - $calypso_env = self::get_calypso_env();
6250 + $calypso_env = ( new Host() )->get_calypso_env();
6598 6251 switch ( $calypso_env ) {
6599 6252 case 'development':
6600 6253 return 'http://calypso.localhost:3000/';
6601 6254 case 'wpcalypso':
@@ -6644,13 +6297,20 @@
6644 6297 }
6645 6298 }
6646 6299
6647 6300 /**
6648 - * Returns a boolean for whether backups UI should be displayed or not.
6301 + * Whether UI for backups should be displayed.
6649 6302 *
6303 + * On WPCom platforms this is gated on the backups-self-serve site feature.
6304 + * On self-hosted Jetpack sites it falls back to the jetpack_show_backups filter.
6305 + *
6650 6306 * @return bool Should backups UI be displayed?
6651 6307 */
6652 6308 public static function show_backups_ui() {
6309 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6310 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'backups-self-serve' );
6311 + }
6312 +
6653 6313 /**
6654 6314 * Whether UI for backups should be displayed.
6655 6315 *
6656 6316 * @since 6.5.0
@@ -6660,8 +6320,24 @@
6660 6320 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
6661 6321 }
6662 6322
6663 6323 /**
6324 + * Whether UI for security scanning should be displayed.
6325 + *
6326 + * On WPCom platforms this is gated on the scan-self-serve site feature.
6327 + * On self-hosted Jetpack sites it always returns true.
6328 + *
6329 + * @return bool Should scan UI be displayed?
6330 + */
6331 + public static function show_scan_ui() {
6332 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6333 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'scan-self-serve' );
6334 + }
6335 +
6336 + return true;
6337 + }
6338 +
6339 + /**
6664 6340 * Clean leftoveruser meta.
6665 6341 *
6666 6342 * Delete Jetpack-related user meta when it is no longer needed.
6667 6343 *
@@ -6733,8 +6409,40 @@
6733 6409 'url' => Redirect::get_url( 'jetpack-faq-backup-disclaimer' ),
6734 6410 ),
6735 6411 );
6736 6412
6413 + $products['creator'] = array(
6414 + 'title' => __( 'Jetpack Creator', 'jetpack' ),
6415 + 'slug' => 'jetpack_creator_yearly',
6416 + 'description' => __( 'Craft stunning content, boost your subscriber base, and monetize your online presence.', 'jetpack' ),
6417 + 'show_promotion' => true,
6418 + 'discount_percent' => 50,
6419 + 'included_in_plans' => array( 'complete' ),
6420 + 'features' => array(
6421 + _x( 'Unlimited subscriber imports', 'Creator Product Feature', 'jetpack' ),
6422 + _x( 'Earn more from your content', 'Creator Product Feature', 'jetpack' ),
6423 + _x( 'Accept payments with PayPal', 'Creator Product Feature', 'jetpack' ),
6424 + _x( 'Increase earnings with WordAds', 'Creator Product Feature', 'jetpack' ),
6425 + ),
6426 + );
6427 +
6428 + $products['growth'] = array(
6429 + 'title' => __( 'Jetpack Growth', 'jetpack' ),
6430 + 'slug' => 'jetpack_growth_yearly',
6431 + 'description' => __( 'Essential tools to help you grow your audience, track visitor engagement, and turn leads into loyal customers and advocates.', 'jetpack' ),
6432 + 'show_promotion' => true,
6433 + 'discount_percent' => 50,
6434 + 'included_in_plans' => array( 'complete' ),
6435 + 'features' => array(
6436 + _x( 'Jetpack Social', 'Growth Product Feature', 'jetpack' ),
6437 + _x( 'Jetpack Stats (10K site views, upgradeable)', 'Growth Product Feature', 'jetpack' ),
6438 + _x( 'Unlimited subscriber imports', 'Growth Product Feature', 'jetpack' ),
6439 + _x( 'Earn more from your content', 'Growth Product Feature', 'jetpack' ),
6440 + _x( 'Accept payments with PayPal', 'Growth Product Feature', 'jetpack' ),
6441 + _x( 'Increase earnings with WordAds', 'Growth Product Feature', 'jetpack' ),
6442 + ),
6443 + );
6444 +
6737 6445 $products['scan'] = array(
6738 6446 'title' => __( 'Jetpack Scan', 'jetpack' ),
6739 6447 'slug' => 'jetpack_scan',
6740 6448 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ),
@@ -6764,9 +6472,9 @@
6764 6472 ),
6765 6473 );
6766 6474
6767 6475 $products['akismet'] = array(
6768 - 'title' => __( 'Akismet Anti-Spam', 'jetpack' ),
6476 + 'title' => __( 'Akismet Anti-spam', 'jetpack' ),
6769 6477 'slug' => 'jetpack_anti_spam',
6770 6478 'description' => __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack' ),
6771 6479 'show_promotion' => true,
6772 6480 'discount_percent' => 50,
@@ -6860,5 +6568,106 @@
6860 6568
6861 6569 return true;
6862 6570 }
6863 6571
6572 + /**
6573 + * Add 5-star review link on the Jetpack Plugin meta, in the plugins list table (on the plugins page).
6574 + *
6575 + * @param array $plugin_meta An array of the plugin's metadata.
6576 + * @param string $plugin_file Path to the plugin file.
6577 + *
6578 + * @return array $plugin_meta An array of the plugin's metadata.
6579 + */
6580 + public function add_5_star_review_link( $plugin_meta, $plugin_file ) {
6581 + if ( $plugin_file !== 'jetpack/jetpack.php' ) {
6582 + return $plugin_meta;
6583 + }
6584 +
6585 + $u = get_current_user_id();
6586 + $site = get_site_url();
6587 +
6588 + $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">'
6589 + . str_repeat( '<span class="dashicons dashicons-star-filled" style="font-size: 16px; width:16px; height: 16px"></span>', 5 )
6590 + . '</a>';
6591 +
6592 + return $plugin_meta;
6593 + }
6594 +
6595 + /**
6596 + * Lazy instantiation of the Plugin_Tracking object.
6597 + *
6598 + * @since 13.9
6599 + *
6600 + * @return void
6601 + */
6602 + public function initialize_tracking() {
6603 + if ( did_action( 'jetpack_initialize_tracking' ) > 1 ) {
6604 + // Only need to run once.
6605 + return;
6606 + }
6607 +
6608 + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) || static::is_connection_ready() ) {
6609 + ( new Plugin_Tracking() )->init();
6610 + }
6611 + }
6612 +
6613 + /**
6614 + * Run the "initialize tracking" hook.
6615 + *
6616 + * @since 13.9
6617 + */
6618 + public function run_initialize_tracking_action() {
6619 + /**
6620 + * Fires when the tracking needs to be initialized.
6621 + * Doesn't necessarily mean that will actually happen, depends if the 'jetpack_tos_agreed' option is set.
6622 + *
6623 + * @since 13.9
6624 + */
6625 + do_action( 'jetpack_initialize_tracking' );
6626 + }
6627 +
6628 + /**
6629 + * Initialize REST jsonAPI if needed.
6630 + *
6631 + * @return void
6632 + */
6633 + public function maybe_initialize_rest_jsonapi() {
6634 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
6635 + if ( ! empty( $_GET['jsonapi'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) {
6636 + require_once ABSPATH . 'wp-admin/includes/admin.php'; // JSON API relies on WP functionality not autoloaded in REST.
6637 +
6638 + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
6639 + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
6640 + }
6641 + }
6642 +
6643 + /**
6644 + * Run plugin post-activation actions if we need to.
6645 + *
6646 + * @return void
6647 + */
6648 + private function plugin_post_activation() {
6649 + if ( ( new Status() )->is_offline_mode() ) {
6650 + return;
6651 + }
6652 +
6653 + if ( get_transient( 'activated_jetpack' ) ) {
6654 + delete_transient( 'activated_jetpack' );
6655 +
6656 + if ( ( new Host() )->is_woa_site() ) {
6657 + $redirect_url = static::admin_url( 'page=jetpack' );
6658 + } elseif ( is_network_admin() ) {
6659 + $redirect_url = admin_url( 'network/admin.php?page=jetpack' );
6660 + } elseif ( get_transient( 'my_jetpack_product_activated' ) ) {
6661 + // don't redirect if this is an activation that just came from My Jetpack
6662 + // My Jetpack has its own set of post-activation redirects
6663 + return;
6664 + } elseif ( My_Jetpack_Initializer::should_initialize() ) {
6665 + $redirect_url = static::admin_url( 'page=my-jetpack' );
6666 + } else {
6667 + $redirect_url = static::admin_url( 'page=jetpack' );
6668 + }
6669 +
6670 + wp_safe_redirect( $redirect_url );
6671 + }
6672 + }
6864 6673 }