PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | class.jetpack.php +1181 -1182 12.0.3 → 16.3-a.1 View file →
@@ -6,31 +6,39 @@
6 6 *
7 7 * @package automattic/jetpack
8 8 */
9 9
10 +use Automattic\Jetpack\Activity_Log\Jetpack_Activity_Log as Activity_Log_Init;
10 11 use Automattic\Jetpack\Assets;
11 -use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
12 +use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
12 13 use Automattic\Jetpack\Config;
14 +use Automattic\Jetpack\Connection\Authorize_Json_Api;
13 15 use Automattic\Jetpack\Connection\Client;
14 16 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
15 -use Automattic\Jetpack\Connection\Nonce_Handler;
16 17 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
17 18 use Automattic\Jetpack\Connection\Secrets;
18 19 use Automattic\Jetpack\Connection\Tokens;
19 -use Automattic\Jetpack\Connection\Utils as Connection_Utils;
20 +use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
20 21 use Automattic\Jetpack\Constants;
21 22 use Automattic\Jetpack\CookieState;
23 +use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
22 24 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
23 25 use Automattic\Jetpack\Errors;
24 26 use Automattic\Jetpack\Files;
27 +use Automattic\Jetpack\Heartbeat;
25 28 use Automattic\Jetpack\Identity_Crisis;
29 +use Automattic\Jetpack\Import\Main as Import_Main;
26 30 use Automattic\Jetpack\Licensing;
27 31 use Automattic\Jetpack\Modules;
28 32 use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
29 -use Automattic\Jetpack\Partner;
33 +use Automattic\Jetpack\Newsletter\Reader_Link;
30 34 use Automattic\Jetpack\Paths;
35 +use Automattic\Jetpack\Plugin\Deprecate;
31 36 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
37 +use Automattic\Jetpack\Podcast\Podcast;
32 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;
33 41 use Automattic\Jetpack\Status;
34 42 use Automattic\Jetpack\Status\Host;
35 43 use Automattic\Jetpack\Status\Visitor;
36 44 use Automattic\Jetpack\Sync\Actions as Sync_Actions;
@@ -37,9 +45,14 @@
37 45 use Automattic\Jetpack\Sync\Health;
38 46 use Automattic\Jetpack\Sync\Sender;
39 47 use Automattic\Jetpack\Terms_Of_Service;
40 48 use Automattic\Jetpack\Tracking;
49 +use Automattic\Woocommerce_Analytics;
41 50
51 +if ( ! defined( 'ABSPATH' ) ) {
52 + exit( 0 );
53 +}
54 +
42 55 /*
43 56 Options:
44 57 jetpack_options (array)
45 58 An array of options.
@@ -67,53 +80,25 @@
67 80 * The Jetpack class.
68 81 */
69 82 class Jetpack {
70 83 /**
71 - * XMLRPC server instance.
84 + * Marks that the durable Jetpack SEO module-state options have been reconciled against
85 + * the site's module configuration, so the repair runs at most once.
72 86 *
73 - * @var null|Jetpack_XMLRPC_Server XMLRPC server used by Jetpack.
87 + * {@see self::reconcile_seo_module_state_options()}
88 + *
89 + * @since 16.1
90 + *
91 + * @var string
74 92 */
75 - public $xmlrpc_server = null;
93 + const SEO_MODULE_STATE_RECONCILED_OPTION = 'jetpack_seo_module_state_reconciled';
76 94
77 95 /**
78 - * The handles of styles that are concatenated into jetpack.css.
96 + * XMLRPC server instance.
79 97 *
80 - * When making changes to that list, you must also update concat_list in tools/webpack.config.css.js.
81 - *
82 - * @var array The handles of styles that are concatenated into jetpack.css.
98 + * @var null|Jetpack_XMLRPC_Server XMLRPC server used by Jetpack.
83 99 */
84 - public $concatenated_style_handles = array(
85 - 'jetpack-carousel-swiper-css',
86 - 'jetpack-carousel',
87 - 'grunion.css',
88 - 'the-neverending-homepage',
89 - 'jetpack_likes',
90 - 'jetpack_related-posts',
91 - 'sharedaddy',
92 - 'jetpack-slideshow',
93 - 'presentations',
94 - 'quiz',
95 - 'jetpack-subscriptions',
96 - 'jetpack-responsive-videos-style',
97 - 'jetpack-social-menu',
98 - 'tiled-gallery',
99 - 'jetpack_display_posts_widget',
100 - 'gravatar-profile-widget',
101 - 'goodreads-widget',
102 - 'jetpack_social_media_icons_widget',
103 - 'jetpack-top-posts-widget',
104 - 'jetpack_image_widget',
105 - 'jetpack-my-community-widget',
106 - 'jetpack-authors-widget',
107 - 'wordads',
108 - 'eu-cookie-law-style',
109 - 'flickr-widget-style',
110 - 'jetpack-search-widget',
111 - 'jetpack-simple-payments-widget-style',
112 - 'jetpack-widget-social-icons-styles',
113 - 'wpcom_instagram_widget',
114 - 'milestone-widget',
115 - );
100 + public $xmlrpc_server = null;
116 101
117 102 /**
118 103 * Contains all assets that have had their URL rewritten to minified versions.
119 104 *
@@ -130,11 +115,8 @@
130 115 'contact-form' => array(
131 116 array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
132 117 array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
133 118 ),
134 - 'custom-css' => array(
135 - array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
136 - ),
137 119 'gravatar-hovercards' => array(
138 120 array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
139 121 ),
140 122 'latex' => array(
@@ -295,21 +277,19 @@
295 277 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
296 278 'Rank Math' => 'seo-by-rank-math/rank-math.php',
297 279 'Slim SEO' => 'slim-seo/slim-seo.php',
298 280 ),
299 - 'lazy-images' => array(
300 - 'Lazy Load' => 'lazy-load/lazy-load.php',
301 - 'BJ Lazy Load' => 'bj-lazy-load/bj-lazy-load.php',
302 - 'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
303 - ),
304 281 );
305 282
306 283 /**
307 284 * Plugins for which we turn off our Facebook OG Tags implementation.
308 285 *
309 - * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
310 - * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
286 + * Note: the following plugins automatically deactivate Jetpack's Open
287 + * Graph tags via filter when their Social Meta modules are active:
311 288 *
289 + * - All in One SEO Pack, All in one SEO Pack Pro
290 + * - WordPress SEO by Yoast, WordPress SEO Premium by Yoast
291 + *
312 292 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
313 293 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
314 294 *
315 295 * @var array Array of plugin slugs.
@@ -351,8 +331,10 @@
351 331 'wp-caregiver/wp-caregiver.php', // WP Caregiver.
352 332 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta.
353 333 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
354 334 'wp-ogp/wp-ogp.php', // WP-OGP.
335 + 'wp-seopress/seopress.php', // SEOPress.
336 + 'wp-seopress-pro/seopress-pro.php', // SEOPress Pro.
355 337 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
356 338 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button.
357 339 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox.
358 340 'seo-by-rank-math/rank-math.php', // Rank Math.
@@ -419,8 +401,10 @@
419 401
420 402 /**
421 403 * Verified data for JSON authorization request
422 404 *
405 + * @deprecated 13.4
406 + *
423 407 * @var array
424 408 */
425 409 public $json_api_authorization_request = array();
426 410
@@ -461,8 +445,16 @@
461 445 */
462 446 public static $instance = false;
463 447
464 448 /**
449 + * Resolved answer for `is_premium_analytics_enabled()`, or null before the first call.
450 + *
451 + * @since 16.1
452 + * @var bool|null
453 + */
454 + private static $premium_analytics_enabled = null;
455 +
456 + /**
465 457 * Singleton
466 458 *
467 459 * @static
468 460 */
@@ -482,17 +474,21 @@
482 474 public function plugin_upgrade() {
483 475 if ( self::is_connection_ready() ) {
484 476 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
485 477 if ( JETPACK__VERSION !== $version ) {
486 - // Prevent multiple upgrades at once - only a single process should trigger
487 - // an upgrade to avoid stampedes.
488 - if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
489 - return;
478 + // Prevent multiple upgrades at once - only a single process should trigger an upgrade to avoid stampedes.
479 + if ( wp_using_ext_object_cache() ) {
480 + if ( true !== wp_cache_add( self::$plugin_upgrade_lock_key, 1, 'transient', 10 ) ) {
481 + return;
482 + }
483 + } else {
484 + if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
485 + return;
486 + }
487 +
488 + set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
490 489 }
491 490
492 - // Set a short lock to prevent multiple instances of the upgrade.
493 - set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
494 -
495 491 // check which active modules actually exist and remove others from active_modules list.
496 492 $unfiltered_modules = self::get_active_modules();
497 493 $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
498 494 if ( array_diff( $unfiltered_modules, $modules ) ) {
@@ -498,9 +494,9 @@
498 494 if ( array_diff( $unfiltered_modules, $modules ) ) {
499 495 self::update_active_modules( $modules );
500 496 }
501 497
502 - add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
498 + self::register_upgrade_init_hooks();
503 499
504 500 // Upgrade to 4.3.0.
505 501 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
506 502 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
@@ -555,8 +551,13 @@
555 551 Jetpack_Options::delete_option( 'autoupdate_plugins' );
556 552 } // Should we have some type of fallback if something fails here?
557 553 }
558 554
555 + // Set the newsletter send default option for existing sites.
556 + if ( false === get_option( 'wpcom_newsletter_send_default' ) ) {
557 + add_option( 'wpcom_newsletter_send_default', 1 );
558 + }
559 +
559 560 if ( did_action( 'wp_loaded' ) ) {
560 561 self::upgrade_on_load();
561 562 } else {
562 563 add_action(
@@ -590,9 +591,8 @@
590 591 }
591 592
592 593 if (
593 594 class_exists( 'Jetpack_Sitemap_Manager' )
594 - && version_compare( JETPACK__VERSION, '5.3', '>=' )
595 595 ) {
596 596 do_action( 'jetpack_sitemaps_purge_data' );
597 597 }
598 598
@@ -607,9 +607,9 @@
607 607 * Also fires Action hooks for each newly activated and deactivated module.
608 608 *
609 609 * @param array $modules Array of active modules to be saved in options.
610 610 *
611 - * @return $success bool true for success, false for failure.
611 + * @return bool $success true for success, false for failure.
612 612 */
613 613 public static function update_active_modules( $modules ) {
614 614 return ( new Modules() )->update_active( $modules );
615 615 }
@@ -639,10 +639,10 @@
639 639 sort( $taken_priorities );
640 640
641 641 $first_priority = array_shift( $taken_priorities );
642 642
643 - if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
644 - $new_priority = - PHP_INT_MAX;
643 + if ( $first_priority <= PHP_INT_MIN ) {
644 + $new_priority = PHP_INT_MIN;
645 645 } else {
646 646 $new_priority = $first_priority - 1;
647 647 }
648 648
@@ -663,32 +663,17 @@
663 663 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
664 664 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
665 665 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
666 666
667 - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
668 -
669 - add_filter(
670 - 'jetpack_signature_check_token',
671 - array( __CLASS__, 'verify_onboarding_token' ),
672 - 10,
673 - 3
674 - );
675 -
676 667 /**
677 668 * Prepare Gutenberg Editor functionality
669 + *
670 + * The hooks previously here have been moved to modules/blocks.php but leaving this here pending
671 + * a longer investigation to see if code is expecting the Gutenberg class to always be available.
678 672 */
679 673 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
680 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
681 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
682 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 );
683 - add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
684 -
685 674 add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
686 675
687 - // Unlink user before deleting the user from WP.com.
688 - add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 );
689 - add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 );
690 -
691 676 add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
692 677
693 678 add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
694 679 add_action( 'login_init', array( $this, 'login_init' ) );
@@ -695,8 +680,13 @@
695 680
696 681 // Set up the REST authentication hooks.
697 682 Connection_Rest_Authentication::init();
698 683
684 + // Register Jetpack-specific connection tests (sync health, etc.) with the connection
685 + // package's health test suite. This runs on all requests (not just admin), because
686 + // the connection/test REST endpoint can be called outside admin context.
687 + add_action( 'jetpack_connection_tests_loaded', array( $this, 'register_jetpack_connection_tests' ) );
688 +
699 689 add_action( 'admin_init', array( $this, 'admin_init' ) );
700 690 add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
701 691
702 692 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
@@ -710,12 +700,8 @@
710 700
711 701 // Returns HTTPS support status.
712 702 add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
713 703
714 - add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
715 -
716 - add_action( 'wp_ajax_jetpack_recommendations_banner', array( 'Jetpack_Recommendations_Banner', 'ajax_callback' ) );
717 -
718 704 add_action( 'wp_loaded', array( $this, 'register_assets' ) );
719 705
720 706 /**
721 707 * These actions run checks to load additional files.
@@ -733,29 +719,8 @@
733 719
734 720 add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
735 721 add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
736 722
737 - require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php';
738 - $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() );
739 - add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) );
740 -
741 - /*
742 - * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
743 - * We should make sure to only do this for front end links.
744 - */
745 - if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
746 - add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
747 - add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
748 -
749 - /*
750 - * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
751 - * so they point moderation links on emails to Calypso.
752 - */
753 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/functions.wp-notify.php';
754 - add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
755 - add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
756 - }
757 -
758 723 add_action(
759 724 'plugins_loaded',
760 725 function () {
761 726 if ( User_Agent_Info::is_mobile_app() ) {
@@ -764,20 +729,12 @@
764 729 }
765 730 );
766 731
767 732 // Update the site's Jetpack plan and products from API on heartbeats.
768 - add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
733 + add_action( 'jetpack_heartbeat', array( Jetpack_Plan::class, 'refresh_from_wpcom' ) );
769 734
770 - /**
771 - * This is the hack to concatenate all css files into one.
772 - * For description and reasoning see the implode_frontend_css method.
773 - *
774 - * Super late priority so we catch all the registered styles.
775 - */
776 - if ( ! is_admin() ) {
777 - add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first.
778 - add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`.
779 - }
735 + // The Connection package fetches the site record for `jetpack/v4/site`; reuse it to refresh the plan.
736 + add_action( 'jetpack_site_data_fetched', array( Jetpack_Plan::class, 'update_from_site_record' ) );
780 737
781 738 // Actually push the stats on shutdown.
782 739 if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
783 740 add_action( 'shutdown', array( $this, 'push_stats' ) );
@@ -785,8 +742,10 @@
785 742
786 743 // After a successful connection.
787 744 add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) );
788 745 add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) );
746 + add_action( 'jetpack_site_registered', array( Reader_Link::class, 'activate_on_connection' ), 9 );
747 + add_action( 'jetpack_site_registered', array( \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::class, 'discard_credentials' ) );
789 748
790 749 // Actions for Manager::authorize().
791 750 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
792 751 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
@@ -791,8 +750,9 @@
791 750 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
792 751 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
793 752 add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
794 753
754 + Jetpack_Client_Server::init();
795 755 add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) );
796 756 add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) );
797 757 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
798 758 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
@@ -797,9 +757,9 @@
797 757 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
798 758 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
799 759
800 760 // Filters for the Manager::get_token() urls and request body.
801 - add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
761 + add_filter( 'jetpack_token_redirect_url', array( Authorize_Redirect::class, 'filter_connect_redirect_url' ) );
802 762 add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
803 763
804 764 // Filter for the `jetpack/v4/connection/data` API response.
805 765 add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) );
@@ -820,9 +780,9 @@
820 780 // Make resources use static domain when possible.
821 781 add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) );
822 782
823 783 // Validate the domain names in Jetpack development versions.
824 - add_action( 'jetpack_pre_register', array( get_called_class(), 'registration_check_domains' ) );
784 + add_action( 'jetpack_pre_register', array( static::class, 'registration_check_domains' ) );
825 785
826 786 // Register product descriptions for partner coupon usage.
827 787 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
828 788
@@ -827,11 +787,143 @@
827 787 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
828 788
829 789 // Actions for conditional recommendations.
830 790 add_action( 'plugins_loaded', array( 'Jetpack_Recommendations', 'init_conditional_recommendation_actions' ) );
791 +
792 + // Add 5-star
793 + add_filter( 'plugin_row_meta', array( $this, 'add_5_star_review_link' ), 10, 2 );
794 + add_action( 'init', array( Deprecate::class, 'instance' ) );
795 +
796 + // Register Jetpack module management abilities (WordPress Abilities API, WP 6.9+).
797 + \Automattic\Jetpack\Plugin\Abilities\Modules_Abilities::init();
798 +
799 + // Register Connection abilities (WordPress Abilities API, WP 6.9+). Scoped to the
800 + // Jetpack plugin for now: the Connection package no longer auto-wires these, so
801 + // connection-only consumers (Boost, Protect, Search, etc.) do not register them yet.
802 + \Automattic\Jetpack\Connection\Abilities\Connection_Abilities::init();
831 803 }
832 804
833 805 /**
806 + * Whether the current request should eagerly initialize the admin/REST-only
807 + * packages (the Import package and My Jetpack) now, at `plugins_loaded` time.
808 + *
809 + * Returns true for admin, cron, POST, and WP-CLI requests — the contexts,
810 + * knowable this early, where those packages have work to do. Returns false
811 + * for a plain front-end GET *and* for a REST request: the two can't be told
812 + * apart yet (this runs before `rest_api_init`), so callers defer the REST
813 + * case by initializing the package on `rest_api_init` instead, while a plain
814 + * page view never fires that hook and so loads nothing. This keeps
815 + * admin/REST-only PHP out of opcache on the front-end GET hot path.
816 + *
817 + * No in-repo code depends on the deferral. The one externally observable
818 + * change is timing: the packages' documented init hooks
819 + * (`jetpack_import_initialized`, `jetpack_feature_import_enabled`, and
820 + * `my_jetpack_init`) no longer fire on a plain front-end GET — they fire on
821 + * the admin, cron, POST, WP-CLI, and REST requests where the packages load.
822 + *
823 + * @return bool
824 + */
825 + private static function should_eager_load_packages() {
826 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) );
827 + $is_wp_cli = Constants::is_true( 'WP_CLI' );
828 +
829 + return is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli;
830 + }
831 +
832 + /**
833 + * Configure the Import package from a deferred hook.
834 + *
835 + * The eager path uses Config::ensure( 'import' ), but the deferred REST path
836 + * runs after Config::on_plugins_loaded() has already processed its feature
837 + * flags, so it needs a hookable bootstrap callback. Preserve Config's
838 + * feature-enabled action for hook consumers.
839 + *
840 + * @since 16.0
841 + *
842 + * @return void
843 + */
844 + public static function configure_import_package() {
845 + if ( class_exists( Import_Main::class ) ) {
846 + Import_Main::configure();
847 +
848 + if ( ! did_action( 'jetpack_feature_import_enabled' ) ) {
849 + do_action( 'jetpack_feature_import_enabled' );
850 + }
851 + }
852 + }
853 +
854 + /**
855 + * Whether the bundled Stats v2 dashboard is enabled.
856 + *
857 + * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
858 + * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
859 + * menu alongside the existing Stats UI; it never replaces or hides the
860 + * legacy Stats menu, admin-bar entries, post-list column, or WP dashboard
861 + * widget. The Stats module's tracking is unaffected either way, and Stats v2
862 + * reads what that module collects, so while the module is off the plugin
863 + * answers false here before even reading the flag.
864 + *
865 + * The package has to be loadable for this to be true, so a site with the
866 + * flag on but a missing package answers false here and never adds the
867 + * Stats v2 menu (a warning is logged instead).
868 + *
869 + * @since 16.1
870 + *
871 + * @return bool
872 + */
873 + public static function is_premium_analytics_enabled() {
874 + if ( null !== self::$premium_analytics_enabled ) {
875 + return self::$premium_analytics_enabled;
876 + }
877 +
878 + if ( ! self::is_module_active( 'stats' ) ) {
879 + self::$premium_analytics_enabled = false;
880 + return false;
881 + }
882 +
883 + /**
884 + * Filters whether the bundled Premium Analytics dashboard is enabled.
885 + *
886 + * Resolved once, from `Jetpack::configure()` on `plugins_loaded`, and only
887 + * while the Stats module is active. Register this from a mu-plugin or a
888 + * plugin's main file — a callback added on `plugins_loaded` or later runs
889 + * too late to be seen.
890 + *
891 + * @since 16.1
892 + *
893 + * @param bool $enabled Defaults to the `jetpack_premium_analytics_enabled` option (false).
894 + */
895 + $flag = (bool) apply_filters( 'jetpack_premium_analytics_enabled', (bool) get_option( 'jetpack_premium_analytics_enabled' ) );
896 +
897 + self::$premium_analytics_enabled = $flag && class_exists( 'Automattic\Jetpack\PremiumAnalytics\Analytics' );
898 +
899 + if ( $flag && ! self::$premium_analytics_enabled ) {
900 + wp_trigger_error(
901 + __METHOD__,
902 + 'The jetpack_premium_analytics_enabled flag is on but the Premium Analytics package is not loadable; keeping the Stats UI in place.'
903 + );
904 + }
905 +
906 + return self::$premium_analytics_enabled;
907 + }
908 +
909 + /**
910 + * Expose the setting that turns the Premium Analytics dashboard on and off.
911 + *
912 + * Deliberately not behind is_premium_analytics_enabled(): this is the setting that flips that
913 + * check, so it has to answer while the dashboard is still off.
914 + *
915 + * @since 16.2
916 + *
917 + * @return void
918 + */
919 + public static function register_premium_analytics_enablement_setting() {
920 + if ( class_exists( 'Automattic\Jetpack\PremiumAnalytics\Enablement_Setting' ) ) {
921 + \Automattic\Jetpack\PremiumAnalytics\Enablement_Setting::register();
922 + }
923 + }
924 +
925 + /**
834 926 * Before everything else starts getting initalized, we need to initialize Jetpack using the
835 927 * Config object.
836 928 */
837 929 public function configure() {
@@ -838,16 +930,12 @@
838 930 $config = new Config();
839 931
840 932 foreach (
841 933 array(
842 - 'blaze',
843 934 'jitm',
844 935 'sync',
936 + 'account_protection',
845 937 'waf',
846 - 'videopress',
847 - 'stats',
848 - 'stats_admin',
849 - 'import',
850 938 )
851 939 as $feature
852 940 ) {
853 941 $config->ensure( $feature );
@@ -852,8 +940,103 @@
852 940 ) {
853 941 $config->ensure( $feature );
854 942 }
855 943
944 + // Enable the VideoPress admin UI (the "Jetpack > VideoPress" dashboard) inside the
945 + // Jetpack plugin, mirroring the standalone Jetpack VideoPress plugin. The dashboard
946 + // only renders when the VideoPress module is active (Status::is_active()); when it
947 + // is not, the menu item links to the My Jetpack interstitial to activate it.
948 + $config->ensure( 'videopress', array( 'admin_ui' => true ) );
949 +
950 + /*
951 + * The Import package only registers `jetpack/v4/import` REST routes — it
952 + * does nothing when rendering a front-end page — so gate its `ensure()`
953 + * to keep its PHP out of opcache on the front-end GET hot path. It still
954 + * loads on admin, cron, POST, and WP-CLI requests, and on `rest_api_init`
955 + * for REST: a REST request can't be identified yet at `plugins_loaded`
956 + * (this runs before `Config::on_plugins_loaded`, and `rest_api_init`
957 + * fires later), so it is initialized directly when that hook fires, while
958 + * a plain page view never fires it and so loads nothing.
959 + *
960 + * JITM stays eager (above): unlike Import, its `register()` adds a
961 + * `jetpack_sync_before_send_updated_option` filter that records the
962 + * `jetpack_last_plugin_sync` transient, and a Jetpack Sync send can fire
963 + * on a plain front-end GET — including the dedicated-sync `spawn-sync`
964 + * GET, which runs on `init` and exits before `rest_api_init`. Deferring
965 + * JITM would skip that bookkeeping and leave its message cache stale after
966 + * a plugin change, so it loads on every request as before.
967 + */
968 + if ( self::should_eager_load_packages() ) {
969 + $config->ensure( 'import' );
970 + } else {
971 + add_action(
972 + 'rest_api_init',
973 + array( __CLASS__, 'configure_import_package' ),
974 + 0
975 + );
976 + }
977 +
978 + /*
979 + * The Stats and Stats Admin packages only do work when the Stats module
980 + * is active (the front-end tracking pixel) or on wp-admin, REST, cron,
981 + * POST, and WP-CLI requests: the Stats dashboard page, the stats /
982 + * stats-app REST endpoints (which the block editor also calls for
983 + * email-open rates), the transient-cleanup cron, the connection
984 + * package's package-version tracker (which runs on POSTs and reads the
985 + * `jetpack_package_versions` filter that Stats registers), and CLI
986 + * introspection such as the heartbeat inspector. On a plain front-end
987 + * GET page view with the module off they are inert: the pixel
988 + * short-circuits on `Stats\Main::should_track()` and every other entry
989 + * point only hooks rest_api_init, admin, cron, the POST-only tracker, or
990 + * is reached through WP-CLI.
991 + * Skip loading them — and eagerly constructing the Stats Admin REST
992 + * controller — on that hot path to keep their PHP out of opcache, but
993 + * keep loading them everywhere else exactly as before so the stats REST
994 + * permission mapping (view_stats, registered by Stats\Main), the
995 + * editor's stats-app calls, the cleanup cron, and the package-version
996 + * tracker are all unchanged.
997 + *
998 + * REST requests are not yet identifiable here (REST_REQUEST is defined
999 + * after plugins_loaded), so defer those to rest_api_init. Call the
1000 + * package initializers directly rather than `$config->ensure()`: ensure()
1001 + * only flags a feature for `Config::on_plugins_loaded()` (plugins_loaded
1002 + * priority 2), which has already run by the time rest_api_init fires.
1003 + * Priority 0 runs before each package's own priority-10 route
1004 + * registration, so their routes still register within the same dispatch.
1005 + * A plain page view never fires rest_api_init, so the packages stay
1006 + * unloaded there. See JETPACK-1747.
1007 + */
1008 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'];
1009 + $is_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
1010 +
1011 + if ( self::is_module_active( 'stats' ) || is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli ) {
1012 + $config->ensure( 'stats' );
1013 + $config->ensure( 'stats_admin' );
1014 + } else {
1015 + add_action(
1016 + 'rest_api_init',
1017 + static function () {
1018 + if ( class_exists( 'Automattic\Jetpack\Stats\Main' ) ) {
1019 + \Automattic\Jetpack\Stats\Main::init();
1020 + }
1021 + if ( class_exists( 'Automattic\Jetpack\Stats_Admin\Main' ) ) {
1022 + \Automattic\Jetpack\Stats_Admin\Main::init();
1023 + }
1024 + },
1025 + 0
1026 + );
1027 + }
1028 +
1029 + // Stats v2 (WOOA7S-1595). Unlike Stats above it cannot be deferred when enabled — see
1030 + // Analytics::init() for why, and for why it takes no menu_title here.
1031 + if ( self::is_premium_analytics_enabled() ) {
1032 + \Automattic\Jetpack\PremiumAnalytics\Analytics::init();
1033 + }
1034 +
1035 + // Outside the check above on purpose — see Enablement_Setting. Deferred like Stats, to keep
1036 + // the autoload off the front-end hot path.
1037 + add_action( 'rest_api_init', array( __CLASS__, 'register_premium_analytics_enablement_setting' ), 0 );
1038 +
856 1039 $config->ensure(
857 1040 'connection',
858 1041 array(
859 1042 'slug' => 'jetpack',
@@ -871,12 +1054,8 @@
871 1054 );
872 1055
873 1056 $config->ensure( 'search' );
874 1057
875 - if ( defined( 'ENABLE_WORDADS_SHARED_UI' ) && ENABLE_WORDADS_SHARED_UI ) {
876 - $config->ensure( 'wordads' );
877 - }
878 -
879 1058 if ( ! $this->connection_manager ) {
880 1059 $this->connection_manager = new Connection_Manager( 'jetpack' );
881 1060 }
882 1061
@@ -884,8 +1063,10 @@
884 1063 if ( $modules->is_active( 'publicize' ) && $this->connection_manager->has_connected_user() ) {
885 1064 $config->ensure( 'publicize' );
886 1065 }
887 1066
1067 + add_action( 'jetpack_initialize_tracking', array( $this, 'initialize_tracking' ) );
1068 +
888 1069 /*
889 1070 * Load things that should only be in Network Admin.
890 1071 *
891 1072 * For now blow away everything else until a more full
@@ -896,10 +1077,13 @@
896 1077 $network = Jetpack_Network::init();
897 1078 $network->set_connection( $this->connection_manager );
898 1079 }
899 1080
900 - if ( self::is_connection_ready() ) {
1081 + $is_connection_ready = self::is_connection_ready();
1082 +
1083 + if ( $is_connection_ready ) {
901 1084 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
1085 + $this->run_initialize_tracking_action();
902 1086
903 1087 Jetpack_Heartbeat::init();
904 1088 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
905 1089 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
@@ -904,8 +1088,19 @@
904 1088 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
905 1089 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
906 1090 Jetpack_Search_Performance_Logger::init();
907 1091 }
1092 + } else {
1093 + add_action( 'jetpack_agreed_to_terms_of_service', array( $this, 'run_initialize_tracking_action' ) );
1094 + add_action( 'rest_api_init', array( $this, 'run_initialize_tracking_action' ) );
1095 + add_filter(
1096 + 'xmlrpc_methods',
1097 + function ( $methods ) {
1098 + $this->run_initialize_tracking_action();
1099 + return $methods;
1100 + },
1101 + 1
1102 + );
908 1103 }
909 1104
910 1105 // Initialize remote file upload request handlers.
911 1106 $this->add_remote_request_handlers();
@@ -912,23 +1107,13 @@
912 1107
913 1108 /*
914 1109 * Enable enhanced handling of previewing sites in Calypso
915 1110 */
916 - if ( self::is_connection_ready() ) {
1111 + if ( $is_connection_ready ) {
917 1112 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
918 1113 add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
919 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
920 - add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
1114 + add_action( 'rest_api_init', array( $this, 'maybe_initialize_rest_jsonapi' ) );
921 1115 }
922 -
923 - if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
924 - add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
925 - } else {
926 - /**
927 - * Initialize tracking right after the user agrees to the terms of service.
928 - */
929 - add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
930 - }
931 1116 }
932 1117
933 1118 /**
934 1119 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
@@ -936,13 +1121,59 @@
936 1121 *
937 1122 * @action plugins_loaded
938 1123 */
939 1124 public function late_initialization() {
940 - add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
1125 + add_action( 'after_setup_theme', array( 'Jetpack', 'load_modules' ), -2 );
941 1126
942 - Partner::init();
943 - My_Jetpack_Initializer::init();
1127 + /*
1128 + * My Jetpack is a wp-admin dashboard. Its Initializer::init() only wires
1129 + * up admin-menu, admin_init, and rest_api_init surfaces — and eagerly
1130 + * loads every product class (backup, boost, protect, …) just to register
1131 + * admin plugin-action links — so none of it is needed on a plain
1132 + * front-end GET page view. (The pieces that do immediate work, e.g.
1133 + * Connection REST authentication and Licensing, are already initialized
1134 + * unconditionally in Jetpack's constructor, so they are unaffected here.)
1135 + *
1136 + * Gate the call to the request types where My Jetpack actually does work.
1137 + * REST can't be detected yet at plugins_loaded, so initialize on
1138 + * rest_api_init for that branch; a plain page view never fires it, so My
1139 + * Jetpack stays unloaded there.
1140 + */
1141 + if ( self::should_eager_load_packages() ) {
1142 + My_Jetpack_Initializer::init();
1143 + } else {
1144 + add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1145 + }
944 1146
1147 + Activity_Log_Init::initialize();
1148 + Scan_Page_Init::initialize();
1149 + Jetpack_SEO_Initializer::init();
1150 +
1151 + if ( ( new Modules() )->is_active( 'podcast' ) ) {
1152 + Podcast::init();
1153 + }
1154 +
1155 + /*
1156 + * Initialize Boost Speed Score. It only does work on REST requests (the
1157 + * dashboard speed-score endpoints) and on a few Jetpack Boost lifecycle
1158 + * actions, so defer constructing it — and loading the boost-speed-score
1159 + * package classes — until one of those hooks actually fires instead of on
1160 + * every request. Priority 0 ensures the object's own callbacks (added in
1161 + * its constructor at the default priority) still run for the firing hook.
1162 + */
1163 + $initialize_speed_score = static function () {
1164 + static $initialized = false;
1165 + if ( $initialized ) {
1166 + return;
1167 + }
1168 + $initialized = true;
1169 + new Speed_Score( array(), 'jetpack-dashboard' );
1170 + };
1171 + add_action( 'rest_api_init', $initialize_speed_score, 0 );
1172 + add_action( 'jetpack_boost_deactivate', $initialize_speed_score, 0 );
1173 + add_action( 'jetpack_boost_environment_changed', $initialize_speed_score, 0 );
1174 + add_action( 'handle_environment_change', $initialize_speed_score, 0 );
1175 +
945 1176 /**
946 1177 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
947 1178 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
948 1179 *
@@ -980,8 +1211,10 @@
980 1211
981 1212 /**
982 1213 * Redirect edit post links to Calypso.
983 1214 *
1215 + * @deprecated since 13.9
1216 + *
984 1217 * @param string $default_url Post edit URL.
985 1218 * @param int $post_id Post ID.
986 1219 *
987 1220 * @return string
@@ -986,8 +1219,10 @@
986 1219 *
987 1220 * @return string
988 1221 */
989 1222 public function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1223 + _deprecated_function( __METHOD__, '13.9' );
1224 +
990 1225 $post = get_post( $post_id );
991 1226
992 1227 if ( empty( $post ) ) {
993 1228 return $default_url;
@@ -1018,13 +1253,17 @@
1018 1253
1019 1254 /**
1020 1255 * Redirect edit comment links to Calypso.
1021 1256 *
1257 + * @deprecated since 13.9
1258 + *
1022 1259 * @param string $url Comment edit URL.
1023 1260 *
1024 1261 * @return string
1025 1262 */
1026 1263 public function point_edit_comment_links_to_calypso( $url ) {
1264 + _deprecated_function( __METHOD__, '13.9' );
1265 +
1027 1266 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1028 1267 $query_args = null;
1029 1268 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1030 1269
@@ -1043,9 +1282,8 @@
1043 1282 *
1044 1283 * @return array list of callables.
1045 1284 */
1046 1285 public function filter_sync_callable_whitelist( $callables ) {
1047 -
1048 1286 // Jetpack Functions.
1049 1287 $jetpack_callables = array(
1050 1288 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1051 1289 'updates' => array( 'Jetpack', 'get_updates' ),
@@ -1050,23 +1288,9 @@
1050 1288 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1051 1289 'updates' => array( 'Jetpack', 'get_updates' ),
1052 1290 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1053 1291 );
1054 - $callables = array_merge( $callables, $jetpack_callables );
1055 -
1056 - // Jetpack_SSO_Helpers.
1057 - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1058 - $sso_helpers = array(
1059 - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1060 - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1061 - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1062 - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1063 - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1064 - );
1065 - $callables = array_merge( $callables, $sso_helpers );
1066 - }
1067 -
1068 - return $callables;
1292 + return array_merge( $callables, $jetpack_callables );
1069 1293 }
1070 1294
1071 1295 /**
1072 1296 * Extend Sync multisite callables with Jetpack Plugin functions.
@@ -1091,41 +1315,8 @@
1091 1315 return $callables;
1092 1316 }
1093 1317
1094 1318 /**
1095 - * Deprecated
1096 - * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1097 - *
1098 - * @param array $params The action parameters.
1099 - *
1100 - * @deprecated since 9.8.
1101 - */
1102 - public function jetpack_track_last_sync_callback( $params ) {
1103 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1104 - return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1105 - }
1106 -
1107 - /**
1108 - * Jetpack Connection banner callback function.
1109 - *
1110 - * @return void
1111 - */
1112 - public function jetpack_connection_banner_callback() {
1113 - check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1114 -
1115 - // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1116 - if (
1117 - isset( $_REQUEST['dismissBanner'] ) &&
1118 - ! Jetpack_Connection_Banner::force_display()
1119 - ) {
1120 - Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1121 - wp_send_json_success();
1122 - }
1123 -
1124 - wp_die();
1125 - }
1126 -
1127 - /**
1128 1319 * If there are any stats that need to be pushed, but haven't been, push them now.
1129 1320 */
1130 1321 public function push_stats() {
1131 1322 if ( ! empty( $this->stats ) ) {
@@ -1140,16 +1331,8 @@
1140 1331 * @param string $cap Capability name.
1141 1332 */
1142 1333 public function jetpack_custom_caps( $caps, $cap ) {
1143 1334 switch ( $cap ) {
1144 - case 'jetpack_manage_modules':
1145 - case 'jetpack_activate_modules':
1146 - case 'jetpack_deactivate_modules':
1147 - $caps = array( 'manage_options' );
1148 - break;
1149 - case 'jetpack_configure_modules':
1150 - $caps = array( 'manage_options' );
1151 - break;
1152 1335 case 'jetpack_manage_autoupdates':
1153 1336 $caps = array(
1154 1337 'manage_options',
1155 1338 'update_plugins',
@@ -1167,9 +1350,9 @@
1167 1350 if ( $is_offline_mode ) {
1168 1351 $caps = array( 'manage_options' );
1169 1352 break;
1170 1353 } else {
1171 - $caps = array( 'read' );
1354 + $caps = array( 'edit_posts' );
1172 1355 }
1173 1356 break;
1174 1357 }
1175 1358 return $caps;
@@ -1239,12 +1422,8 @@
1239 1422 * Register the social logos
1240 1423 */
1241 1424 require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1242 1425 jetpack_register_social_logos();
1243 -
1244 - if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1245 - wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1246 - }
1247 1426 }
1248 1427
1249 1428 /**
1250 1429 * Guess locale from language code.
@@ -1332,9 +1511,9 @@
1332 1511 }
1333 1512 /**
1334 1513 * Does the network allow admins to add new users.
1335 1514 *
1336 - * @return boolian
1515 + * @return bool
1337 1516 */
1338 1517 public static function network_add_new_users() {
1339 1518 return (bool) get_site_option( 'add_new_users' );
1340 1519 }
@@ -1341,9 +1520,9 @@
1341 1520 /**
1342 1521 * File upload psace left per site in MB.
1343 1522 * -1 means NO LIMIT.
1344 1523 *
1345 - * @return number
1524 + * @return int
1346 1525 */
1347 1526 public static function network_site_upload_space() {
1348 1527 // value in MB.
1349 1528 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
@@ -1360,9 +1539,9 @@
1360 1539
1361 1540 /**
1362 1541 * Maximum file upload size set by the network.
1363 1542 *
1364 - * @return number
1543 + * @return int
1365 1544 */
1366 1545 public static function network_max_upload_file_size() {
1367 1546 // value in KB.
1368 1547 return get_site_option( 'fileupload_maxk', 300 );
@@ -1521,9 +1700,9 @@
1521 1700 }
1522 1701 return 0;
1523 1702 }
1524 1703
1525 -// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1704 + // phpcs:disable WordPress.WP.CapitalPDangit.MisspelledInComment
1526 1705 /**
1527 1706 * Gets updates and stores in jetpack_updates.
1528 1707 *
1529 1708 * The jetpack_updates option is saved in the following schema:
@@ -1539,8 +1718,9 @@
1539 1718 *
1540 1719 * @return array
1541 1720 */
1542 1721 public static function get_updates() {
1722 + $updates = array();
1543 1723 $update_data = wp_get_update_data();
1544 1724
1545 1725 // Stores the individual update counts as well as the total count.
1546 1726 if ( isset( $update_data['counts'] ) ) {
@@ -1553,11 +1733,11 @@
1553 1733 if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1554 1734 $updates['wp_update_version'] = $cur->current;
1555 1735 }
1556 1736 }
1557 - return isset( $updates ) ? $updates : array();
1737 + return $updates;
1558 1738 }
1559 - // phpcs:enable
1739 + // phpcs:enable WordPress.WP.CapitalPDangit.MisspelledInComment
1560 1740
1561 1741 /**
1562 1742 * Get update details for core, plugins, and themes.
1563 1743 *
@@ -1615,44 +1795,8 @@
1615 1795 return apply_filters( 'jetpack_is_connection_ready', self::connection()->is_connected(), self::connection() );
1616 1796 }
1617 1797
1618 1798 /**
1619 - * Deprecated: Is Jetpack in development (offline) mode?
1620 - *
1621 - * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1622 - * and themes still use it, and we do not want to flood them with notices.
1623 - *
1624 - * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1625 - *
1626 - * @deprecated since 8.0.
1627 - */
1628 - public static function is_development_mode() {
1629 - _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1630 - return ( new Status() )->is_offline_mode();
1631 - }
1632 -
1633 - /**
1634 - * Whether the site is currently onboarding or not.
1635 - * A site is considered as being onboarded if it currently has an onboarding token.
1636 - *
1637 - * @since 5.8
1638 - * @deprecated Use \Automattic\Jetpack\Status()->is_onboarding()
1639 - *
1640 - * @access public
1641 - * @static
1642 - *
1643 - * @return bool True if the site is currently onboarding, false otherwise
1644 - */
1645 - public static function is_onboarding() {
1646 - _deprecated_function( __METHOD__, 'jetpack-10.9', 'Automattic\\Jetpack\\Status\\is_onboarding' );
1647 -
1648 - if ( ! method_exists( 'Automattic\Jetpack\Status', 'is_onboarding' ) ) {
1649 - return Jetpack_Options::get_option( 'onboarding' ) !== false;
1650 - }
1651 - return ( new Status() )->is_onboarding();
1652 - }
1653 -
1654 - /**
1655 1799 * Determines reason for Jetpack offline mode.
1656 1800 */
1657 1801 public static function development_mode_trigger_text() {
1658 1802 $status = new Status();
@@ -1666,11 +1810,10 @@
1666 1810 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1667 1811 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1668 1812 } elseif ( $status->is_local_site() ) {
1669 1813 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1670 - /** This filter is documented in packages/status/src/class-status.php */
1671 - } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1672 - $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1814 + } elseif ( get_option( 'jetpack_offline_mode' ) ) {
1815 + $notice = __( 'The jetpack_offline_mode option is set to true.', 'jetpack' );
1673 1816 } else {
1674 1817 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1675 1818 }
1676 1819
@@ -1685,9 +1828,9 @@
1685 1828 if ( ( new Status() )->is_offline_mode() ) {
1686 1829 $notice = sprintf(
1687 1830 /* translators: %s is a URL */
1688 1831 __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1689 - Redirect::get_url( 'jetpack-support-development-mode' )
1832 + esc_url( Redirect::get_url( 'jetpack-support-development-mode' ) )
1690 1833 );
1691 1834
1692 1835 $notice .= ' ' . self::development_mode_trigger_text();
1693 1836
@@ -1696,19 +1839,12 @@
1696 1839
1697 1840 // Throw up a notice if using a development version and as for feedback.
1698 1841 if ( self::is_development_version() ) {
1699 1842 /* translators: %s is a URL */
1700 - $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' ) );
1843 + $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' ) ) );
1701 1844
1702 1845 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1703 1846 }
1704 - // Throw up a notice if using staging mode.
1705 - if ( ( new Status() )->is_staging_site() ) {
1706 - /* translators: %s is a URL */
1707 - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1708 -
1709 - echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1710 - }
1711 1847 }
1712 1848
1713 1849 /**
1714 1850 * Whether Jetpack's version maps to a public release, or a development version.
@@ -1729,28 +1865,8 @@
1729 1865 );
1730 1866 }
1731 1867
1732 1868 /**
1733 - * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1734 - *
1735 - * @param int $user_id User ID or will use get_current_user_id if false/not provided.
1736 - */
1737 - public static function is_user_connected( $user_id = false ) {
1738 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1739 - return self::connection()->is_user_connected( $user_id );
1740 - }
1741 -
1742 - /**
1743 - * Get the wpcom user data of the current|specified connected user.
1744 - *
1745 - * @param null|int $user_id User ID or will use get_current_user_id if null.
1746 - */
1747 - public static function get_connected_user_data( $user_id = null ) {
1748 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1749 - return self::connection()->get_connected_user_data( $user_id );
1750 - }
1751 -
1752 - /**
1753 1869 * Get the wpcom email of the current|specified connected user.
1754 1870 *
1755 1871 * @param null|int $user_id User ID or will use get_current_user_id if null.
1756 1872 */
@@ -1802,18 +1918,11 @@
1802 1918 */
1803 1919 public static function load_modules() {
1804 1920 $status = new Status();
1805 1921
1806 - if ( method_exists( $status, 'is_onboarding' ) ) {
1807 - $is_onboarding = $status->is_onboarding();
1808 - } else {
1809 - $is_onboarding = self::is_onboarding();
1810 - }
1811 -
1812 1922 if (
1813 1923 ! self::is_connection_ready()
1814 1924 && ! $status->is_offline_mode()
1815 - && ! $is_onboarding
1816 1925 && (
1817 1926 ! is_multisite()
1818 1927 || ! get_site_option( 'jetpack_protect_active' )
1819 1928 )
@@ -1934,22 +2043,10 @@
1934 2043 *
1935 2044 * @todo Store the result in core's object cache maybe?
1936 2045 */
1937 2046 public static function get_active_plugins() {
1938 - $active_plugins = (array) get_option( 'active_plugins', array() );
1939 -
1940 - if ( is_multisite() ) {
1941 - // Due to legacy code, active_sitewide_plugins stores them in the keys,
1942 - // whereas active_plugins stores them in the values.
1943 - $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1944 - if ( $network_plugins ) {
1945 - $active_plugins = array_merge( $active_plugins, $network_plugins );
1946 - }
1947 - }
1948 -
1949 - sort( $active_plugins );
1950 -
1951 - return array_unique( $active_plugins );
2047 + // Delegates to the canonical implementation in the Connection package.
2048 + return Heartbeat::get_active_plugins();
1952 2049 }
1953 2050
1954 2051 /**
1955 2052 * Gets and parses additional plugin data to send with the heartbeat data
@@ -2087,8 +2184,10 @@
2087 2184 *
2088 2185 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2089 2186 */
2090 2187 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2188 + // @todo Remove this require once the deprecated Jetpack_Twitter_Cards wrapper has been removed.
2189 + // Twitter Cards functionality now lives in the jetpack-post-media package (Automattic\Jetpack\Post_Media\Twitter_Cards).
2091 2190 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2092 2191 }
2093 2192 }
2094 2193
@@ -2191,9 +2290,9 @@
2191 2290 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.
2192 2291 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2193 2292 }
2194 2293 wp_safe_redirect( self::admin_url( 'page=' . rawurlencode( $page ) ) );
2195 - exit;
2294 + exit( 0 );
2196 2295 }
2197 2296 }
2198 2297
2199 2298 /**
@@ -2273,12 +2372,14 @@
2273 2372 * @return array
2274 2373 */
2275 2374 public function handle_deprecated_modules( $modules ) {
2276 2375 $deprecated_modules = array(
2277 - 'debug' => null, // Closed out and moved to the debugger library.
2278 - 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2279 - 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2280 - 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2376 + 'debug' => null, // Closed out and moved to the debugger library.
2377 + 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2378 + 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2379 + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2380 + 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2381 + 'enhanced-distribution' => null, // Closed out in 13.3 -- WP.com is winding down the firehose.
2281 2382 );
2282 2383
2283 2384 // Don't activate SSO if they never completed activating WPCC.
2284 2385 if ( self::is_module_active( 'wpcc' ) ) {
@@ -2332,8 +2433,17 @@
2332 2433 }
2333 2434 }
2334 2435 }
2335 2436
2437 + // Special case to convert block setting to a block module.
2438 + $block_key = array_search( 'blocks', $modules, true );
2439 + if ( $block_key !== false ) { // Only care if 'blocks' made it through the previous filters.
2440 + $block_option = get_option( 'jetpack_blocks_disabled', null );
2441 + if ( $block_option ) {
2442 + unset( $modules[ $block_key ] );
2443 + }
2444 + }
2445 +
2336 2446 return $modules;
2337 2447 }
2338 2448
2339 2449 /**
@@ -2406,8 +2516,11 @@
2406 2516 if ( isset( $module['description'] ) ) {
2407 2517 $modules[ $index ]['description'] = $i18n_module['description'];
2408 2518 $modules[ $index ]['short_description'] = $i18n_module['description'];
2409 2519 }
2520 + if ( isset( $module['module_tags'] ) ) {
2521 + $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2522 + }
2410 2523 }
2411 2524 return $modules;
2412 2525 }
2413 2526
@@ -2443,20 +2556,28 @@
2443 2556
2444 2557 /**
2445 2558 * Catches PHP errors. Must be used in conjunction with output buffering.
2446 2559 *
2560 + * @deprecated since 13.5
2447 2561 * @param bool $catch True to start catching, False to stop.
2448 2562 *
2449 2563 * @static
2564 + * @deprecated 13.5
2565 + * @see \Automattic\Jetpack\Errors
2450 2566 */
2451 2567 public static function catch_errors( $catch ) {
2568 + _deprecated_function( __METHOD__, '13.5' );
2569 + // @phan-suppress-next-line PhanDeprecatedClass
2452 2570 return ( new Errors() )->catch_errors( $catch );
2453 2571 }
2454 2572
2455 2573 /**
2456 2574 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2575 + *
2576 + * @deprecated since 13.5
2457 2577 */
2458 2578 public static function catch_errors_on_shutdown() {
2579 + _deprecated_function( __METHOD__, '13.5' );
2459 2580 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2460 2581 }
2461 2582
2462 2583 /**
@@ -2548,9 +2669,9 @@
2548 2669 }
2549 2670
2550 2671 if ( $deactivated ) {
2551 2672 if ( $send_state_messages ) {
2552 - self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2673 + self::state( 'deactivated_plugins', implode( ',', $deactivated ) );
2553 2674 }
2554 2675
2555 2676 if ( $redirect ) {
2556 2677 $url = add_query_arg(
@@ -2560,9 +2681,9 @@
2560 2681 ),
2561 2682 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2562 2683 );
2563 2684 wp_safe_redirect( $url );
2564 - exit;
2685 + exit( 0 );
2565 2686 }
2566 2687 }
2567 2688
2568 2689 /**
@@ -2580,9 +2701,8 @@
2580 2701
2581 2702 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating.
2582 2703 if ( $send_state_messages ) {
2583 2704 self::restate();
2584 - self::catch_errors( true );
2585 2705 }
2586 2706
2587 2707 $active = self::get_active_modules();
2588 2708
@@ -2650,10 +2770,8 @@
2650 2770 if ( $send_state_messages ) {
2651 2771 self::state( 'error', false );
2652 2772 self::state( 'module', false );
2653 2773 }
2654 -
2655 - self::catch_errors( false );
2656 2774 /**
2657 2775 * Fires when default modules are activated.
2658 2776 *
2659 2777 * @since 1.9.0
@@ -2731,9 +2849,9 @@
2731 2849 *
2732 2850 * @param string $message Error message.
2733 2851 * @param bool $deactivate Deactivate Jetpack or not.
2734 2852 *
2735 - * @return void
2853 + * @return never
2736 2854 */
2737 2855 public static function bail_on_activation( $message, $deactivate = true ) {
2738 2856 ?>
2739 2857 <!doctype html>
@@ -2771,9 +2889,9 @@
2771 2889 if ( $update ) {
2772 2890 update_option( 'active_plugins', array_filter( $plugins ) );
2773 2891 }
2774 2892 }
2775 - exit;
2893 + exit( 0 );
2776 2894 }
2777 2895
2778 2896 /**
2779 2897 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
@@ -2798,12 +2916,18 @@
2798 2916 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
2799 2917
2800 2918 Health::on_jetpack_activated();
2801 2919
2920 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
2921 +
2802 2922 if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) {
2803 2923 Sync_Actions::do_only_first_initial_sync();
2804 2924 }
2805 2925
2926 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
2927 + Woocommerce_Analytics::maybe_add_proxy_speed_module();
2928 + }
2929 +
2806 2930 self::plugin_initialize();
2807 2931 }
2808 2932
2809 2933 /**
@@ -2838,9 +2962,9 @@
2838 2962
2839 2963 if ( $plugins_path === $referer['path'] ) {
2840 2964 $source_type = 'list';
2841 2965 } elseif ( $plugins_install_path === $referer['path'] ) {
2842 - $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
2966 + $tab = $query_parts['tab'] ?? 'featured';
2843 2967 switch ( $tab ) {
2844 2968 case 'popular':
2845 2969 $source_type = 'popular';
2846 2970 break;
@@ -2850,10 +2974,10 @@
2850 2974 case 'favorites':
2851 2975 $source_type = 'favorites';
2852 2976 break;
2853 2977 case 'search':
2854 - $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
2855 - $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
2978 + $source_type = 'search-' . ( $query_parts['type'] ?? 'term' );
2979 + $source_query = $query_parts['s'] ?? null;
2856 2980 break;
2857 2981 default:
2858 2982 $source_type = 'featured';
2859 2983 }
@@ -2862,29 +2986,351 @@
2862 2986 return array( $source_type, $source_query );
2863 2987 }
2864 2988
2865 2989 /**
2866 - * Runs before bumping version numbers up to a new version
2990 + * Runs before bumping version numbers up to a new version.
2867 2991 *
2868 - * @param string $version Version:timestamp.
2992 + * Only ever registered the hooks for the release post update modal, which has been removed.
2993 + * No longer hooked to `updating_jetpack_version`.
2994 + *
2995 + * @deprecated 16.2
2996 + *
2997 + * @param string $version Version:timestamp.
2869 2998 * @param string $old_version Old Version:timestamp or false if not set yet.
2870 2999 */
2871 - public static function do_version_bump( $version, $old_version ) {
2872 - if ( $old_version ) { // For existing Jetpack installations.
2873 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3000 + public static function do_version_bump( $version, $old_version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Signature preserved for the deprecation shim.
3001 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
3002 + }
2874 3003
2875 - // If a front end page is visited after the update, the 'wp' action will fire.
2876 - add_action( 'wp', 'Jetpack::set_update_modal_display' );
3004 + /**
3005 + * Enables the Newsletter (subscriptions) module for existing sites now that it is a default-on module.
3006 + *
3007 + * Fresh installs receive the module via its "Auto Activate: Yes" header, so this only handles sites
3008 + * upgrading from a version where the module defaulted off. It runs once per site (guarded by the
3009 + * subscriptions_default_on_migrated option). Fresh installs are marked as migrated immediately so the
3010 + * migration never runs for them. After it has run, the user's choice to deactivate the module again
3011 + * (for example from the My Jetpack Products page) is respected and never reverted.
3012 + *
3013 + * The module requires a connection, so on a disconnected site the migration is deferred without setting
3014 + * the guard, allowing a later version bump to retry once the site is connected.
3015 + *
3016 + * @param string $version New Jetpack version:timestamp.
3017 + * @param string|false $old_version Previous Jetpack version:timestamp, or false on a fresh install.
3018 + */
3019 + public static function activate_subscriptions_module_for_existing_sites( $version, $old_version ) {
3020 + if ( get_option( 'jetpack_subscriptions_default_on_migrated' ) ) {
3021 + return;
3022 + }
2877 3023
2878 - // If an admin page is visited after the update, the 'current_screen' action will fire.
2879 - add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3024 + // Fresh installs get the module via its "Auto Activate: Yes" header. Mark them as migrated so a
3025 + // later opt-out is never reverted by the existing-site path on a subsequent version bump.
3026 + if ( ! $old_version ) {
3027 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3028 + return;
2880 3029 }
3030 +
3031 + if ( ! self::is_connection_ready() ) {
3032 + return;
3033 + }
3034 +
3035 + // Mark as migrated only once the module is active, so a transient activation failure is retried on
3036 + // a later version bump rather than being silently skipped.
3037 + if ( self::is_module_active( 'subscriptions' ) || self::activate_module( 'subscriptions', false, false ) ) {
3038 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3039 + }
2881 3040 }
2882 3041
2883 3042 /**
3043 + * Option flag that records the AI master-switch opt-out reconciliation has run,
3044 + * so it never runs twice.
3045 + *
3046 + * @var string
3047 + */
3048 + const AI_MASTER_OPTOUT_MIGRATED_OPTION = 'jetpack_ai_master_optout_migrated';
3049 +
3050 + /**
3051 + * Register the on-upgrade init hooks whose relative ORDER matters, extracted so
3052 + * the ordering can be asserted in tests without invoking plugin_upgrade() (whose
3053 + * guards make it unreliable to trigger under test). activate_new_modules()
3054 + * (init, default priority 10) auto-activates "Auto Activate: Yes" modules
3055 + * including the `ai` master; reconcile_ai_master_optout() must run at a LATER
3056 + * priority to honor an explicit AI opt-out.
3057 + *
3058 + * @return void
3059 + */
3060 + public static function register_upgrade_init_hooks() {
3061 + add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
3062 + add_action( 'init', array( __CLASS__, 'reconcile_ai_master_optout' ), 20 );
3063 + }
3064 +
3065 + /**
3066 + * Preserves an explicit Jetpack AI opt-out when the `ai` module becomes the site-wide
3067 + * master switch off WordPress.com Simple (self-hosted and Atomic).
3068 + *
3069 + * The `ai` module is "Auto Activate: Yes", so on upgrade {@see self::activate_new_modules()}
3070 + * turns it on for connected sites — the desired default-on / auto-enable-on-connection
3071 + * behavior, which this method deliberately leaves alone. The one case it corrects is a site
3072 + * that had explicitly disabled Jetpack AI (the `jetpack_ai_enabled` option present and falsey)
3073 + * before the module shipped: that opt-out must survive the module becoming the master, so the
3074 + * module is deactivated for exactly those sites. An absent or truthy option is left untouched.
3075 + *
3076 + * Ordering is the whole point. `activate_new_modules()` is hooked on `init` at priority 10 and
3077 + * auto-activates the module there; this method is hooked on `init` at priority 20 (see
3078 + * {@see self::plugin_upgrade()}), so it runs AFTER the auto-activation and its deactivation is
3079 + * the final state. A version-guarded block that ran inline during `plugins_loaded` would be
3080 + * undone by the later auto-activation, which is why this is a late-init hook rather than an
3081 + * inline upgrade step.
3082 + *
3083 + * WordPress.com Simple never runs modules — the option stays the master there — so this is a
3084 + * no-op on Simple. The {@see self::AI_MASTER_OPTOUT_MIGRATED_OPTION} flag makes it run exactly
3085 + * once, which matters because off-Simple the option is no longer the master after this runs:
3086 + * a stale falsey option must not keep re-deactivating a module the user later turns back on.
3087 + *
3088 + * @return void
3089 + */
3090 + public static function reconcile_ai_master_optout() {
3091 + if ( get_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION ) ) {
3092 + return;
3093 + }
3094 +
3095 + // Simple keeps the `jetpack_ai_enabled` option as the master; modules don't run there.
3096 + if ( ( new Host() )->is_wpcom_simple() ) {
3097 + return;
3098 + }
3099 +
3100 + // A sentinel default distinguishes an absent option (leave auto-activation alone) from one
3101 + // explicitly stored falsey (an opt-out to preserve).
3102 + $stored = get_option( 'jetpack_ai_enabled', 'not-set' );
3103 + if ( 'not-set' !== $stored && ! (bool) $stored ) {
3104 + ( new Modules() )->deactivate( 'ai' );
3105 + }
3106 +
3107 + update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3108 + }
3109 +
3110 + /**
3111 + * Whether a module should be recorded as active in its durable Jetpack SEO option.
3112 + *
3113 + * Permanent module overrides are part of the site's configuration, so the normal
3114 + * filtered module state is authoritative. The exception is wpcomsh's private-site
3115 + * callback, which temporarily suppresses `sitemaps` on Atomic sites without changing
3116 + * the site's configured state. Evaluate the module state with only that callback
3117 + * disabled.
3118 + *
3119 + * The hook is cloned before removing the callback so its ordering remains unchanged for
3120 + * the rest of the request. `$available_only` is false because these migrations can run
3121 + * after the standalone module file has been removed.
3122 + *
3123 + * WordPress.com Simple keeps module state outside this site's options table, so its
3124 + * normal filtered read remains authoritative.
3125 + *
3126 + * @since 16.1
3127 + *
3128 + * @param string $module Module slug.
3129 + * @return bool Whether the module should be recorded as active.
3130 + */
3131 + private static function is_module_active_for_seo_option( $module ) {
3132 + $modules = new Modules();
3133 +
3134 + if ( ( new Host() )->is_wpcom_simple() ) {
3135 + return $modules->is_active( $module, false );
3136 + }
3137 +
3138 + global $wp_filter;
3139 +
3140 + $hook_name = 'jetpack_active_modules';
3141 + $private_site_callback = '\Private_Site\filter_jetpack_active_modules';
3142 + $callback_priority = has_filter( $hook_name, $private_site_callback );
3143 +
3144 + if ( false === $callback_priority ) {
3145 + return $modules->is_active( $module, false );
3146 + }
3147 +
3148 + $original_hook = $wp_filter[ $hook_name ];
3149 + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Use an isolated hook copy without changing callback order.
3150 + $wp_filter[ $hook_name ] = clone $original_hook;
3151 + remove_filter( $hook_name, $private_site_callback, $callback_priority );
3152 +
3153 + try {
3154 + return $modules->is_active( $module, false );
3155 + } finally {
3156 + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore the untouched original hook.
3157 + $wp_filter[ $hook_name ] = $original_hook;
3158 + }
3159 + }
3160 +
3161 + /**
3162 + * Records whether the standalone Sitemaps module is active so the setting survives
3163 + * the module's removal.
3164 + *
3165 + * The Jetpack SEO product reads the {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION}
3166 + * option instead of the `sitemaps` module's active state. Module-active state is
3167 + * filtered against the modules present on disk, so once the standalone module is
3168 + * removed it would read as inactive even for sites that had it on. This one-time
3169 + * migration captures the configured module state — which persists regardless of
3170 + * whether the module file is present — into the durable option.
3171 + *
3172 + * Deliberately non-destructive: it never touches generated sitemap data
3173 + * (`jp_sitemap*` posts), the `jetpack-sitemap-state` option, sitemap settings, or the
3174 + * `jp_sitemap_cron_hook` cron, so no regeneration is triggered. `add_option()` only
3175 + * seeds when the option is absent, so it is safe to run on every version bump and
3176 + * never reverts a value the user has since set.
3177 + *
3178 + * Hooked on `updating_jetpack_version`; the version arguments are not needed because
3179 + * `add_option()` provides the run-once guard.
3180 + */
3181 + public static function migrate_sitemaps_module_to_seo_option() {
3182 + // Ignore wpcomsh's temporary private-site suppression while preserving permanent
3183 + // module overrides. {@see self::is_module_active_for_seo_option()}.
3184 + $sitemaps_active = self::is_module_active_for_seo_option( 'sitemaps' );
3185 +
3186 + add_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, $sitemaps_active );
3187 + }
3188 +
3189 + /**
3190 + * Keeps the Jetpack SEO sitemap option in sync with the legacy `sitemaps` module
3191 + * while both still exist.
3192 + *
3193 + * Hooked to the module's activate/deactivate actions, so toggling sitemaps from any
3194 + * surface (the legacy Traffic settings, the SEO Settings tab, or WP-CLI) keeps the
3195 + * durable {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} option current. The
3196 + * actions fire after `active_modules` is updated, so the configured state already
3197 + * reflects the new choice. Ignore temporary private-site suppression without bypassing
3198 + * permanent module overrides. Removed alongside the module itself.
3199 + */
3200 + public static function sync_seo_sitemap_option() {
3201 + update_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, self::is_module_active_for_seo_option( 'sitemaps' ) );
3202 + }
3203 +
3204 + /**
3205 + * Records whether the standalone Canonical URLs module is active so the setting survives
3206 + * the module's removal.
3207 + *
3208 + * The Jetpack SEO product reads the {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION}
3209 + * option instead of the `canonical-urls` module's active state. Module-active state is
3210 + * filtered against the modules present on disk, so once the standalone module is
3211 + * removed it would read as inactive even for sites that had it on. This one-time
3212 + * migration captures the configured module state — which persists regardless of
3213 + * whether the module file is present — into the durable option.
3214 + *
3215 + * Deliberately non-destructive: `add_option()` only seeds when the option is absent, so
3216 + * it is safe to run on every version bump and never reverts a value the user has since
3217 + * set.
3218 + *
3219 + * Hooked on `updating_jetpack_version`; the version arguments are not needed because
3220 + * `add_option()` provides the run-once guard.
3221 + */
3222 + public static function migrate_canonical_urls_module_to_seo_option() {
3223 + $canonical_active = self::is_module_active_for_seo_option( 'canonical-urls' );
3224 +
3225 + add_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, $canonical_active );
3226 + }
3227 +
3228 + /**
3229 + * Keeps the Jetpack SEO canonical-urls option in sync with the legacy `canonical-urls`
3230 + * module while both still exist.
3231 + *
3232 + * Hooked to the module's activate/deactivate actions, so toggling canonical URLs from any
3233 + * surface (the legacy Traffic settings, the SEO Settings tab, or WP-CLI) keeps the
3234 + * durable {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION} option current. The
3235 + * actions fire after `active_modules` is updated, so the configured state already
3236 + * reflects the new choice. Permanent module overrides remain authoritative. Removed
3237 + * alongside the module itself.
3238 + */
3239 + public static function sync_seo_canonical_urls_option() {
3240 + update_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, self::is_module_active_for_seo_option( 'canonical-urls' ) );
3241 + }
3242 +
3243 + /**
3244 + * Repairs durable SEO module-state options that the first pass of the migration seeded
3245 + * from a filtered read.
3246 + *
3247 + * Jetpack 16.0 seeded {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} and
3248 + * {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION} through
3249 + * {@see Modules::is_active()}, which passes the `jetpack_active_modules` filter. A site
3250 + * that was private at the time therefore recorded `sitemaps` as off even though the user
3251 + * had it on, and because the migration seeds with `add_option()` the value was never
3252 + * revisited — making the site public again did not restore the setting.
3253 + *
3254 + * This runs once and rewrites both options from the site's configured module state,
3255 + * including permanent module overrides. That is safe against a choice the user has made
3256 + * since: every surface that toggles these settings (the legacy Traffic page, the SEO
3257 + * Settings tab, WP-CLI) goes through {@see Modules::activate()}/{@see Modules::deactivate()},
3258 + * so the module state and durable option are already in agreement wherever the original
3259 + * migration got it right.
3260 + *
3261 + * Idempotent: the marker is written with `add_option()`, so reruns on later version bumps
3262 + * are no-ops. Like the migrations it seeds from, it touches no sitemap data or cron state.
3263 + *
3264 + * @since 16.1
3265 + */
3266 + public static function reconcile_seo_module_state_options() {
3267 + if ( get_option( self::SEO_MODULE_STATE_RECONCILED_OPTION ) ) {
3268 + return;
3269 + }
3270 +
3271 + update_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, self::is_module_active_for_seo_option( 'sitemaps' ) );
3272 + update_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, self::is_module_active_for_seo_option( 'canonical-urls' ) );
3273 +
3274 + add_option( self::SEO_MODULE_STATE_RECONCILED_OPTION, true );
3275 + }
3276 +
3277 + /**
3278 + * Wires up the migration + sync hooks that keep the durable Jetpack SEO module-state
3279 + * options ({@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} /
3280 + * {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION}) seeded and in sync with their
3281 + * legacy modules. Called once from `load-jetpack.php`.
3282 + *
3283 + * Extracted from file scope so the wiring is unit-testable (file-scope `add_action()`
3284 + * calls run during bootstrap and can't be exercised by a test). Removed alongside the
3285 + * modules in the deferred post-convergence follow-up that absorbs them into Jetpack SEO.
3286 + */
3287 + public static function register_seo_module_migration_hooks() {
3288 + add_action( 'updating_jetpack_version', array( 'Jetpack', 'migrate_sitemaps_module_to_seo_option' ) );
3289 + add_action( 'jetpack_activate_module_sitemaps', array( 'Jetpack', 'sync_seo_sitemap_option' ) );
3290 + add_action( 'jetpack_deactivate_module_sitemaps', array( 'Jetpack', 'sync_seo_sitemap_option' ) );
3291 +
3292 + add_action( 'updating_jetpack_version', array( 'Jetpack', 'migrate_canonical_urls_module_to_seo_option' ) );
3293 + add_action( 'jetpack_activate_module_canonical-urls', array( 'Jetpack', 'sync_seo_canonical_urls_option' ) );
3294 + add_action( 'jetpack_deactivate_module_canonical-urls', array( 'Jetpack', 'sync_seo_canonical_urls_option' ) );
3295 +
3296 + // Runs after both migrations above (default priority, registered last) so a freshly
3297 + // seeded site is already correct and the reconciliation is a no-op there.
3298 + add_action( 'updating_jetpack_version', array( 'Jetpack', 'reconcile_seo_module_state_options' ) );
3299 + }
3300 +
3301 + /**
3302 + * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3303 + * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3304 + *
3305 + * Hooked on `updating_jetpack_version`, which fires on every install including the
3306 + * first — with `$old_version === false` on a brand-new site (the same signal
3307 + * {@see self::activate_subscriptions_module_for_existing_sites()} keys off). Fresh
3308 + * installs are seeded visible; existing installs are seeded hidden and opt in later
3309 + * via the legacy Traffic page or My Jetpack. `add_option()` makes this seed-once: it
3310 + * never overrides a value a later opt-in (or opt-out) has set. WordPress.com sites
3311 + * ignore this option entirely (always visible) — see
3312 + * {@see \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()}.
3313 + *
3314 + * @param string $version The new Jetpack version (unused).
3315 + * @param string|false $old_version The previous version, or false on a fresh install.
3316 + */
3317 + public static function seed_seo_visibility_cohort( $version, $old_version ) {
3318 + add_option( Jetpack_SEO_Initializer::VISIBILITY_OPTION, ! $old_version );
3319 + }
3320 +
3321 + /**
2884 3322 * Sets the display_update_modal state.
3323 + *
3324 + * The release post update modal that read this state has been removed. The write is kept so the
3325 + * method still behaves as documented for the deprecation window. When this is deleted, also drop
3326 + * the matching `display_update_modal` guard in Automattic\Jetpack\CookieState::should_set_cookie(),
3327 + * which exists only to keep this key out of the cookie on the Jetpack admin screen.
3328 + *
3329 + * @deprecated 16.2
2885 3330 */
2886 3331 public static function set_update_modal_display() {
3332 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2887 3333 self::state( 'display_update_modal', true );
2888 3334 }
2889 3335
2890 3336 /**
@@ -2889,11 +3335,16 @@
2889 3335
2890 3336 /**
2891 3337 * Enqueues the block library styles.
2892 3338 *
3339 + * Only ever used by the release post update modal, which has been removed.
3340 + *
3341 + * @deprecated 16.2
3342 + *
2893 3343 * @param string $hook The current admin page.
2894 3344 */
2895 3345 public static function enqueue_block_style( $hook ) {
3346 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2896 3347 if ( 'toplevel_page_jetpack' === $hook ) {
2897 3348 wp_enqueue_style( 'wp-block-library' );
2898 3349 }
2899 3350 }
@@ -2922,9 +3373,8 @@
2922 3373
2923 3374 self::load_modules();
2924 3375
2925 3376 Jetpack_Options::delete_option( 'do_activate' );
2926 - Jetpack_Options::delete_option( 'dismissed_connection_banner' );
2927 3377 }
2928 3378
2929 3379 /**
2930 3380 * Handles the activation of the default modules depending on the current state of the site:
@@ -2983,8 +3433,12 @@
2983 3433 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
2984 3434 self::disconnect();
2985 3435 Jetpack_Options::delete_option( 'version' );
2986 3436 }
3437 +
3438 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
3439 + Woocommerce_Analytics::maybe_remove_proxy_speed_module();
3440 + }
2987 3441 }
2988 3442
2989 3443 /**
2990 3444 * Set activated option to 4 on jetpack_idc_disconnect action.
@@ -3012,9 +3466,9 @@
3012 3466 $connection->remove_connection( ! Identity_Crisis::validate_sync_error_idc_option() );
3013 3467 }
3014 3468
3015 3469 /**
3016 - * Happens after a successfull disconnection.
3470 + * Happens after a successful disconnection.
3017 3471 *
3018 3472 * @static
3019 3473 */
3020 3474 public static function jetpack_site_disconnected() {
@@ -3019,8 +3473,10 @@
3019 3473 */
3020 3474 public static function jetpack_site_disconnected() {
3021 3475 Identity_Crisis::clear_all_idc_options();
3022 3476
3477 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
3478 +
3023 3479 // Delete all the sync related data. Since it could be taking up space.
3024 3480 Sender::get_instance()->uninstall();
3025 3481
3026 3482 /**
@@ -3037,10 +3493,13 @@
3037 3493 }
3038 3494 }
3039 3495
3040 3496 /**
3041 - * Disconnects the user
3497 + * Disconnects the user.
3042 3498 *
3499 + * @deprecated 13.4
3500 + * @see \Automattic\Jetpack\Connection\Manager::disconnect_user()
3501 + *
3043 3502 * @param int $user_id The user ID to disconnect.
3044 3503 */
3045 3504 public function disconnect_user( $user_id ) {
3046 3505 $this->connection_manager->disconnect_user( $user_id );
@@ -3046,21 +3505,8 @@
3046 3505 $this->connection_manager->disconnect_user( $user_id );
3047 3506 }
3048 3507
3049 3508 /**
3050 - * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3051 - *
3052 - * @deprecated since Jetpack 9.7.0
3053 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
3054 - *
3055 - * @return bool|WP_Error
3056 - */
3057 - public static function try_registration() {
3058 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3059 - return static::connection()->try_registration();
3060 - }
3061 -
3062 - /**
3063 3509 * Checking the domain names in beta versions.
3064 3510 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3065 3511 *
3066 3512 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
@@ -3067,9 +3513,9 @@
3067 3513 *
3068 3514 * @return null|\WP_Error The domain validation error, or `null` if everything's fine.
3069 3515 */
3070 3516 public static function registration_check_domains( $error ) {
3071 - if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3517 + if ( static::is_development_version() ) {
3072 3518 $domains_to_check = array_unique(
3073 3519 array(
3074 3520 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3075 3521 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
@@ -3094,10 +3540,17 @@
3094 3540 * @param mixed $code Error code to log.
3095 3541 * @param mixed $data Data to log.
3096 3542 */
3097 3543 public static function log( $code, $data = null ) {
3544 +
3545 + $raw_log = Jetpack_Options::get_option( 'log', array() );
3546 + // This can be modified by the `jetpack_options` filter, so abort if we don't have an array.
3547 + if ( ! is_array( $raw_log ) ) {
3548 + return;
3549 + }
3550 +
3098 3551 // only grab the latest 200 entries.
3099 - $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3552 + $log = array_slice( $raw_log, -199, 199 );
3100 3553
3101 3554 // Append our event to the log.
3102 3555 $log_entry = array(
3103 3556 'time' => time(),
@@ -3197,8 +3650,15 @@
3197 3650
3198 3651 /**
3199 3652 * Return stat data for WPCOM sync.
3200 3653 *
3654 + * The Sync stats module was this method's last caller and moved to the Connection package's
3655 + * `Heartbeat::generate_stats_array()`, which assembles the heartbeat data through the
3656 + * `jetpack_heartbeat_stats_array` filter. Note the package method does not include the extended
3657 + * data from `get_additional_stat_data()`, so callers relying on `$extended` need to add it themselves.
3658 + *
3659 + * @deprecated 16.2
3660 + *
3201 3661 * @param bool $encode JSON encode the result.
3202 3662 * @param bool $extended Adds additional stats data.
3203 3663 *
3204 3664 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
@@ -3203,10 +3663,13 @@
3203 3663 *
3204 3664 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
3205 3665 */
3206 3666 public static function get_stat_data( $encode = true, $extended = true ) {
3207 - $data = Jetpack_Heartbeat::generate_stats_array();
3667 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3208 3668
3669 + // Site environment stats now live in the Connection package; merge them with the Jetpack-specific stats.
3670 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), Heartbeat::get_environment_stats() );
3671 +
3209 3672 if ( $extended ) {
3210 3673 $additional_data = self::get_additional_stat_data();
3211 3674 $data = array_merge( $data, $additional_data );
3212 3675 }
@@ -3211,9 +3674,9 @@
3211 3674 $data = array_merge( $data, $additional_data );
3212 3675 }
3213 3676
3214 3677 if ( $encode ) {
3215 - return wp_json_encode( $data );
3678 + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
3216 3679 }
3217 3680
3218 3681 return $data;
3219 3682 }
@@ -3285,38 +3748,34 @@
3285 3748 $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
3286 3749 /** Already documented in automattic/jetpack-connection::src/class-client.php */
3287 3750 $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false );
3288 3751
3289 - if ( ! $is_offline_mode ) {
3290 - Jetpack_Connection_Banner::init();
3291 - Jetpack_Connection_Widget::init();
3292 - }
3752 + // Run post-activation actions if needed.
3753 + $this->plugin_post_activation();
3293 3754
3294 3755 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3295 3756 // Upgrade: 1.1 -> 1.1.1
3296 3757 // Check and see if host can verify the Jetpack servers' SSL certificate.
3297 3758 $args = array();
3759 + // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
3298 3760 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3299 3761 }
3300 3762
3301 - Jetpack_Recommendations_Banner::init();
3302 -
3303 - if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3763 + if (
3764 + current_user_can( 'manage_options' )
3765 + && ! self::permit_ssl()
3766 + && ! $is_offline_mode
3767 + ) {
3304 3768 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3305 3769 }
3306 3770
3307 3771 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3308 3772 add_action( 'load-plugins.php', array( $this, 'plugins_page_init_jetpack_state' ) );
3309 - add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3310 3773
3311 3774 if ( ! ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) && ! is_network_admin() ) ) {
3312 3775 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3313 3776 }
3314 3777
3315 - if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3316 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3317 - }
3318 -
3319 3778 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3320 3779
3321 3780 if ( self::is_connection_ready() || $is_offline_mode ) {
3322 3781 // Artificially throw errors in certain specific cases during plugin activation.
@@ -3321,13 +3780,8 @@
3321 3780 if ( self::is_connection_ready() || $is_offline_mode ) {
3322 3781 // Artificially throw errors in certain specific cases during plugin activation.
3323 3782 add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3324 3783 }
3325 -
3326 - // Add custom column in wp-admin/users.php to show whether user is linked.
3327 - add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3328 - add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3329 - add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3330 3784 }
3331 3785
3332 3786 /**
3333 3787 * Adds body classes.
@@ -3360,8 +3814,9 @@
3360 3814 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3361 3815 * This function artificially throws errors for such cases (per a specific list).
3362 3816 *
3363 3817 * @param string $plugin The activated plugin.
3818 + * @throws RuntimeException If a conflicting plugin is detected.
3364 3819 */
3365 3820 public function throw_error_on_activate_plugin( $plugin ) {
3366 3821 $active_modules = self::get_active_modules();
3367 3822
@@ -3374,8 +3829,9 @@
3374 3829 if ( 'stats.php' === basename( $plugin ) ) {
3375 3830 $throw = true;
3376 3831 }
3377 3832 } else {
3833 + // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Checked above. See also https://github.com/phan/phan/issues/1204.
3378 3834 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3379 3835 if ( basename( $plugin ) === basename( $reflection->getFileName() ) ) {
3380 3836 $throw = true;
3381 3837 }
@@ -3382,9 +3838,9 @@
3382 3838 }
3383 3839
3384 3840 if ( $throw ) {
3385 3841 /* translators: Plugin name to deactivate. */
3386 - 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
3842 + throw new RuntimeException( sprintf( __( 'Jetpack contains the most recent version of the old "%1$s" plugin.', 'jetpack' ), 'WordPress.com Stats' ) );
3387 3843 }
3388 3844 }
3389 3845 }
3390 3846
@@ -3466,8 +3922,9 @@
3466 3922 /**
3467 3923 * Handler for Jetpack remote file uploads.
3468 3924 *
3469 3925 * @access public
3926 + * @return never
3470 3927 */
3471 3928 public function remote_request_handlers() {
3472 3929 switch ( current_filter() ) {
3473 3930 case 'wp_ajax_nopriv_jetpack_upload_file':
@@ -3494,18 +3951,17 @@
3494 3951 if ( ! is_int( $status_code ) ) {
3495 3952 $status_code = 400;
3496 3953 }
3497 3954
3498 - status_header( $status_code );
3499 - die( wp_json_encode( (object) compact( 'error', 'error_description' ) ) );
3955 + wp_send_json( (object) compact( 'error', 'error_description' ), $status_code, JSON_UNESCAPED_SLASHES );
3500 3956 }
3501 3957
3502 - status_header( 200 );
3503 3958 if ( true === $response ) {
3504 - exit;
3959 + status_header( 200 );
3960 + exit( 0 );
3505 3961 }
3506 3962
3507 - die( wp_json_encode( (object) $response ) );
3963 + wp_send_json( (object) $response, 200, JSON_UNESCAPED_SLASHES );
3508 3964 }
3509 3965
3510 3966 /**
3511 3967 * Uploads a file gotten from the global $_FILES.
@@ -3513,9 +3969,9 @@
3513 3969 * the attachment file of the media item (gotten through of the post_id)
3514 3970 * will be updated instead of add a new one.
3515 3971 *
3516 3972 * @param boolean $update_media_item - update media attachment.
3517 - * @return array - An array describing the uploadind files process.
3973 + * @return array|WP_Error - An array describing the uploading files process.
3518 3974 */
3519 3975 public function upload_handler( $update_media_item = false ) {
3520 3976 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
3521 3977 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
@@ -3566,9 +4022,9 @@
3566 4022 return new WP_Error( 'handler_cannot_upload', __( 'The upload handler cannot upload files', 'jetpack' ), 400 );
3567 4023 }
3568 4024
3569 4025 $uploaded_files = array();
3570 - $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
4026 + $global_post = $GLOBALS['post'] ?? null;
3571 4027 unset( $GLOBALS['post'] );
3572 4028 if ( empty( $_FILES['media']['name'] ) ) {
3573 4029 // Nothing to process, just return.
3574 4030 return $uploaded_files;
@@ -3575,9 +4031,9 @@
3575 4031 }
3576 4032 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.
3577 4033 $file = array();
3578 4034 foreach ( $media_keys as $media_key ) {
3579 - $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.
4035 + $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.
3580 4036 }
3581 4037
3582 4038 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.
3583 4039
@@ -3623,9 +4079,9 @@
3623 4079 'type' => (string) $edited_media_item->post_mime_type,
3624 4080 'meta' => (array) wp_get_attachment_metadata( $post_id ),
3625 4081 );
3626 4082
3627 - return (array) array( $response );
4083 + return array( $response );
3628 4084 }
3629 4085
3630 4086 $attachment_id = media_handle_upload(
3631 4087 '.jetpack.upload.',
@@ -3664,94 +4120,8 @@
3664 4120 return $uploaded_files;
3665 4121 }
3666 4122
3667 4123 /**
3668 - * Add help to the Jetpack page
3669 - *
3670 - * @since Jetpack (1.2.3)
3671 - * @return void
3672 - */
3673 - public function admin_help() {
3674 - $current_screen = get_current_screen();
3675 -
3676 - // Overview.
3677 - $current_screen->add_help_tab(
3678 - array(
3679 - 'id' => 'home',
3680 - 'title' => __( 'Home', 'jetpack' ),
3681 - 'content' =>
3682 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3683 - '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3684 - '<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>',
3685 - )
3686 - );
3687 -
3688 - // Screen Content.
3689 - if ( current_user_can( 'manage_options' ) ) {
3690 - $current_screen->add_help_tab(
3691 - array(
3692 - 'id' => 'settings',
3693 - 'title' => __( 'Settings', 'jetpack' ),
3694 - 'content' =>
3695 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3696 - '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3697 - '<ol>' .
3698 - '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3699 - '<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>' .
3700 - '</ol>' .
3701 - '<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>',
3702 - )
3703 - );
3704 - }
3705 -
3706 - // Help Sidebar.
3707 - $support_url = Redirect::get_url( 'jetpack-support' );
3708 - $faq_url = Redirect::get_url( 'jetpack-faq' );
3709 - $current_screen->set_help_sidebar(
3710 - '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3711 - '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3712 - '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3713 - '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3714 - );
3715 - }
3716 -
3717 - /**
3718 - * Enqueues the jetpack-icons style.
3719 - *
3720 - * @return void
3721 - */
3722 - public function admin_menu_css() {
3723 - wp_enqueue_style( 'jetpack-icons' );
3724 - }
3725 -
3726 - /**
3727 - * Registers/enqueues Jetpack banner styles.
3728 - *
3729 - * @return void
3730 - */
3731 - public function admin_banner_styles() {
3732 - $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3733 -
3734 - if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3735 - wp_register_style(
3736 - 'jetpack-dops-style',
3737 - plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3738 - array(), // Load styles for components so the modal can be used.
3739 - JETPACK__VERSION
3740 - );
3741 - }
3742 -
3743 - wp_enqueue_style(
3744 - 'jetpack',
3745 - plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3746 - array( 'jetpack-dops-style' ),
3747 - JETPACK__VERSION . '-20121016'
3748 - );
3749 - wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3750 - wp_style_add_data( 'jetpack', 'suffix', $min );
3751 - }
3752 -
3753 - /**
3754 4124 * Add action links for the Jetpack plugin.
3755 4125 *
3756 4126 * @param array $actions Plugin actions.
3757 4127 *
@@ -3757,14 +4127,11 @@
3757 4127 *
3758 4128 * @return array
3759 4129 */
3760 4130 public function plugin_action_links( $actions ) {
3761 - $support_link = ( new Host() )->is_woa_site() ? 'https://wordpress.com/help/contact/' : self::admin_url( 'page=jetpack-debugger' );
3762 -
3763 4131 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
3764 4132 return array_merge(
3765 - array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3766 - array( 'support' => sprintf( '<a href="%s">%s</a>', $support_link, __( 'Support', 'jetpack' ) ) ),
4133 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3767 4134 $actions
3768 4135 );
3769 4136 }
3770 4137
@@ -3792,14 +4159,10 @@
3792 4159 'jetpack-plugins-page-js',
3793 4160 '_inc/build/plugins-page.js',
3794 4161 JETPACK__PLUGIN_FILE,
3795 4162 array(
3796 - 'in_footer' => true,
3797 - 'textdomain' => 'jetpack',
3798 - 'dependencies' => array(
3799 - 'wp-polyfill',
3800 - 'wp-components',
3801 - ),
4163 + 'in_footer' => true,
4164 + 'textdomain' => 'jetpack',
3802 4165 )
3803 4166 );
3804 4167 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3805 4168
@@ -3804,9 +4167,9 @@
3804 4167 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3805 4168
3806 4169 // Add objects to be passed to the initial state of the app.
3807 4170 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3808 - 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' );
4171 + wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
3809 4172
3810 4173 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
3811 4174 }
3812 4175 }
@@ -3811,9 +4174,9 @@
3811 4174 }
3812 4175 }
3813 4176
3814 4177 /**
3815 - * Outputs the wrapper for the plugin deactivation modal
4178 + * Outputs the wrapper for the plugin modal
3816 4179 * Contents are loaded by React script
3817 4180 *
3818 4181 * @return void
3819 4182 */
@@ -3855,9 +4218,9 @@
3855 4218 // @todo provide way to go to specific calypso env.
3856 4219 self::get_calypso_host() . 'jetpack/connect'
3857 4220 )
3858 4221 );
3859 - exit;
4222 + exit( 0 );
3860 4223 }
3861 4224 }
3862 4225
3863 4226 /*
@@ -3892,8 +4255,28 @@
3892 4255 * Done!
3893 4256 */
3894 4257
3895 4258 /**
4259 + * Build the user-facing description stored alongside a registration error code.
4260 + *
4261 + * @since 16.2
4262 + *
4263 + * @param string $error_code The WP_Error code.
4264 + * @param string $message The WP_Error message.
4265 + * @return string The description, empty when the message is not user-facing copy.
4266 + */
4267 + public static function get_registration_error_description( $error_code, $message ) {
4268 + // Manager::validate_remote_register_response() does not always put user-facing copy in the
4269 + // message slot: wpcom_5??, wpcom_408 and wpcom_bad_response store the HTTP status there,
4270 + // and jetpack_id stores the raw response body, which can also overflow the state cookie.
4271 + if ( 'jetpack_id' === $error_code || is_numeric( $message ) ) {
4272 + return '';
4273 + }
4274 +
4275 + return mb_substr( (string) $message, 0, 250 );
4276 + }
4277 +
4278 + /**
3896 4279 * Handles the page load events for the Jetpack admin page
3897 4280 */
3898 4281 public function admin_page_load() {
3899 4282 $error = false;
@@ -3929,10 +4312,11 @@
3929 4312 $registered = static::connection()->try_registration();
3930 4313 if ( is_wp_error( $registered ) ) {
3931 4314 $error = $registered->get_error_code();
3932 4315 self::state( 'error', $error );
3933 - self::state( 'error', $registered->get_error_message() );
3934 4316
4317 + self::state( 'error_description', self::get_registration_error_description( $error, $registered->get_error_message() ) );
4318 +
3935 4319 /**
3936 4320 * Jetpack registration Error.
3937 4321 *
3938 4322 * @since 7.5.0
@@ -3956,12 +4340,8 @@
3956 4340 do_action( 'jetpack_connection_register_success', $from );
3957 4341
3958 4342 $url = $this->build_connect_url( true, $redirect, $from );
3959 4343
3960 - if ( ! empty( $_GET['onboarding'] ) ) {
3961 - $url = add_query_arg( 'onboarding', rawurlencode_deep( wp_unslash( $_GET['onboarding'] ) ), $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3962 - }
3963 -
3964 4344 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
3965 4345 $url = add_query_arg( 'auth_approved', 'true', $url );
3966 4346 }
3967 4347
@@ -3966,9 +4346,9 @@
3966 4346 }
3967 4347
3968 4348 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3969 4349 wp_safe_redirect( $url );
3970 - exit;
4350 + exit( 0 );
3971 4351 case 'activate':
3972 4352 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3973 4353 $error = 'cheatin';
3974 4354 break;
@@ -3982,9 +4362,9 @@
3982 4362 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
3983 4363 }
3984 4364 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3985 4365 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3986 - exit;
4366 + exit( 0 );
3987 4367 case 'activate_default_modules':
3988 4368 check_admin_referer( 'activate_default_modules' );
3989 4369 self::log( 'activate_default_modules' );
3990 4370 self::restate();
@@ -3992,9 +4372,9 @@
3992 4372 $max_version = isset( $_GET['max_version'] ) ? sanitize_text_field( wp_unslash( $_GET['max_version'] ) ) : false;
3993 4373 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_GET['other_modules'] ) ) : array();
3994 4374 self::activate_default_modules( $min_version, $max_version, $other_modules );
3995 4375 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3996 - exit;
4376 + exit( 0 );
3997 4377 case 'disconnect':
3998 4378 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3999 4379 $error = 'cheatin';
4000 4380 break;
@@ -4003,9 +4383,9 @@
4003 4383 check_admin_referer( 'jetpack-disconnect' );
4004 4384 self::log( 'disconnect' );
4005 4385 self::disconnect();
4006 4386 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4007 - exit;
4387 + exit( 0 );
4008 4388 case 'reconnect':
4009 4389 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4010 4390 $error = 'cheatin';
4011 4391 break;
@@ -4016,9 +4396,9 @@
4016 4396 self::disconnect();
4017 4397
4018 4398 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4019 4399 wp_safe_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4020 - exit;
4400 + exit( 0 );
4021 4401 case 'deactivate':
4022 4402 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4023 4403 $error = 'cheatin';
4024 4404 break;
@@ -4032,9 +4412,9 @@
4032 4412 self::state( 'message', 'module_deactivated' );
4033 4413 }
4034 4414 self::state( 'module', $modules );
4035 4415 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4036 - exit;
4416 + exit( 0 );
4037 4417 case 'unlink':
4038 4418 $redirect = isset( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : '';
4039 4419 check_admin_referer( 'jetpack-unlink' );
4040 4420 self::log( 'unlink' );
@@ -4044,32 +4424,9 @@
4044 4424 wp_safe_redirect( admin_url() );
4045 4425 } else {
4046 4426 wp_safe_redirect( self::admin_url( array( 'page' => rawurlencode( $redirect ) ) ) );
4047 4427 }
4048 - exit;
4049 - case 'onboard':
4050 - if ( ! current_user_can( 'manage_options' ) ) {
4051 - wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4052 - } else {
4053 - self::create_onboarding_token();
4054 - $url = $this->build_connect_url( true );
4055 -
4056 - $token = Jetpack_Options::get_option( 'onboarding' );
4057 -
4058 - if ( false !== ( $token ) ) {
4059 - $url = add_query_arg( 'onboarding', $token, $url );
4060 - }
4061 -
4062 - $calypso_env = $this->get_calypso_env();
4063 - if ( ! empty( $calypso_env ) ) {
4064 - $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4065 - }
4066 -
4067 - add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4068 - wp_safe_redirect( $url );
4069 - exit;
4070 - }
4071 - exit;
4428 + exit( 0 );
4072 4429 default:
4073 4430 /**
4074 4431 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4075 4432 *
@@ -4085,9 +4442,10 @@
4085 4442 if ( ! $error ) {
4086 4443 self::activate_new_modules( true );
4087 4444 }
4088 4445
4089 - $message_code = self::state( 'message' );
4446 + $activated_manage = false;
4447 + $message_code = self::state( 'message' );
4090 4448 if ( self::state( 'optin-manage' ) ) {
4091 4449 $activated_manage = $message_code;
4092 4450 $message_code = 'jetpack-manage';
4093 4451 }
@@ -4226,9 +4584,9 @@
4226 4584 $module_slugs[] = $module_slug;
4227 4585 $module_names[] = "<strong>{$module['name']}</strong>";
4228 4586 }
4229 4587
4230 - $module_slugs = join( ',', $module_slugs );
4588 + $module_slugs = implode( ',', $module_slugs );
4231 4589 ?>
4232 4590 <div id="message" class="jetpack-message jetpack-err">
4233 4591 <div class="squeezer">
4234 4592 <h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
@@ -4261,17 +4619,19 @@
4261 4619 count( $privacy_checks ),
4262 4620 '%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4263 4621 'jetpack'
4264 4622 ),
4265 - wp_nonce_url(
4266 - self::admin_url(
4267 - array(
4268 - 'page' => 'jetpack',
4269 - 'action' => 'deactivate',
4270 - 'module' => rawurlencode( $module_slugs ),
4271 - )
4272 - ),
4273 - "jetpack_deactivate-$module_slugs"
4623 + esc_url(
4624 + wp_nonce_url(
4625 + self::admin_url(
4626 + array(
4627 + 'page' => 'jetpack',
4628 + 'action' => 'deactivate',
4629 + 'module' => rawurlencode( $module_slugs ),
4630 + )
4631 + ),
4632 + "jetpack_deactivate-$module_slugs"
4633 + )
4274 4634 ),
4275 4635 esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4276 4636 ),
4277 4637 array(
@@ -4289,37 +4649,8 @@
4289 4649 endif;
4290 4650 }
4291 4651
4292 4652 /**
4293 - * We can't always respond to a signed XML-RPC request with a
4294 - * helpful error message. In some circumstances, doing so could
4295 - * leak information.
4296 - *
4297 - * Instead, track that the error occurred via a Jetpack_Option,
4298 - * and send that data back in the heartbeat.
4299 - * All this does is increment a number, but it's enough to find
4300 - * trends.
4301 - *
4302 - * @param WP_Error $xmlrpc_error The error produced during
4303 - * signature validation.
4304 - */
4305 - public function track_xmlrpc_error( $xmlrpc_error ) {
4306 - $code = is_wp_error( $xmlrpc_error )
4307 - ? $xmlrpc_error->get_error_code()
4308 - : 'should-not-happen';
4309 -
4310 - $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4311 - if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4312 - // No need to update the option if we already have
4313 - // this code stored.
4314 - return;
4315 - }
4316 - $xmlrpc_errors[ $code ] = true;
4317 -
4318 - Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4319 - }
4320 -
4321 - /**
4322 4653 * Initialize the jetpack stats instance only when needed
4323 4654 *
4324 4655 * @return void
4325 4656 */
@@ -4417,9 +4748,9 @@
4417 4748 if ( is_network_admin() ) {
4418 4749 $url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4419 4750 }
4420 4751
4421 - $calypso_env = self::get_calypso_env();
4752 + $calypso_env = ( new Host() )->get_calypso_env();
4422 4753
4423 4754 if ( ! empty( $calypso_env ) ) {
4424 4755 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4425 4756 }
@@ -4442,9 +4773,9 @@
4442 4773 return $this->build_connect_url( $raw, $redirect, $from, true );
4443 4774 }
4444 4775 }
4445 4776
4446 - $url = $this->build_authorize_url( $redirect );
4777 + $url = ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4447 4778 }
4448 4779
4449 4780 if ( $from ) {
4450 4781 $url = add_query_arg( 'from', $from, $url );
@@ -4469,66 +4800,30 @@
4469 4800 * @param null $deprecated Deprecated since Jetpack 10.9.
4470 4801 *
4471 4802 * @todo Update default value for redirect since the called function expects a string.
4472 4803 *
4804 + * @deprecated 13.4
4805 + *
4473 4806 * @return mixed|void
4474 4807 */
4475 4808 public static function build_authorize_url( $redirect = false, $deprecated = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
4809 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::build_authorize_url' );
4476 4810
4477 - add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4478 - add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4479 -
4480 - $c8n = self::connection();
4481 - $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4482 -
4483 - remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4484 - remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4485 -
4486 - /**
4487 - * Filter the URL used when authorizing a user to a WordPress.com account.
4488 - *
4489 - * @since 8.9.0
4490 - *
4491 - * @param string $url Connection URL.
4492 - */
4493 - return apply_filters( 'jetpack_build_authorize_url', $url );
4811 + return ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4494 4812 }
4495 4813
4496 4814 /**
4497 4815 * Filters the connection URL parameter array.
4498 4816 *
4817 + * @deprecated 13.4
4818 + *
4499 4819 * @param array $args default URL parameters used by the package.
4500 4820 * @return array the modified URL arguments array.
4501 4821 */
4502 4822 public static function filter_connect_request_body( $args ) {
4503 - if (
4504 - Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4505 - && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4506 - ) {
4507 - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4508 - $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4509 - ? $gp_locale->slug
4510 - : '';
4511 - }
4823 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_request_body' );
4512 4824
4513 - $tracking = new Tracking();
4514 - $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4515 -
4516 - $args = array_merge(
4517 - $args,
4518 - array(
4519 - '_ui' => $tracks_identity['_ui'],
4520 - '_ut' => $tracks_identity['_ut'],
4521 - )
4522 - );
4523 -
4524 - $calypso_env = self::get_calypso_env();
4525 -
4526 - if ( ! empty( $calypso_env ) ) {
4527 - $args['calypso_env'] = $calypso_env;
4528 - }
4529 -
4530 - return $args;
4825 + return Authorize_Redirect::filter_connect_request_body( $args );
4531 4826 }
4532 4827
4533 4828 /**
4534 4829 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
@@ -4565,41 +4860,19 @@
4565 4860 return $current_user_connection_data;
4566 4861 }
4567 4862
4568 4863 /**
4569 - * Filters the URL that will process the connection data. It can be different from the URL
4570 - * that we send the user to after everything is done.
4571 - *
4572 - * @param String $processing_url the default redirect URL used by the package.
4573 - * @return String the modified URL.
4574 - *
4575 - * @deprecated since Jetpack 9.5.0
4576 - */
4577 - public static function filter_connect_processing_url( $processing_url ) {
4578 - _deprecated_function( __METHOD__, 'jetpack-9.5' );
4579 -
4580 - $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4581 - return $processing_url;
4582 - }
4583 -
4584 - /**
4585 4864 * Filters the redirection URL that is used for connect requests. The redirect
4586 4865 * URL should return the user back to the Jetpack console.
4587 4866 *
4867 + * @deprecated 13.4
4868 + *
4588 4869 * @param String $redirect the default redirect URL used by the package.
4589 4870 * @return String the modified URL.
4590 4871 */
4591 4872 public static function filter_connect_redirect_url( $redirect ) {
4592 - $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4593 - $redirect = $redirect
4594 - ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4595 - : $jetpack_admin_page;
4596 -
4597 - if ( isset( $_REQUEST['is_multisite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- not making a site change here.
4598 - $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4599 - }
4600 -
4601 - return $redirect;
4873 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_redirect_url' );
4874 + return Authorize_Redirect::filter_connect_redirect_url( $redirect );
4602 4875 }
4603 4876
4604 4877 /**
4605 4878 * This action fires at the beginning of the Manager::authorize method.
@@ -4663,11 +4936,8 @@
4663 4936 *
4664 4937 * @param array $data The request data.
4665 4938 */
4666 4939 public static function authorize_ending_authorized( $data ) {
4667 - // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4668 - self::invalidate_onboarding_token();
4669 -
4670 4940 // If redirect_uri is SSO, ensure SSO module is enabled.
4671 4941 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4672 4942
4673 4943 /** This filter is documented in class.jetpack-cli.php */
@@ -4791,13 +5061,9 @@
4791 5061 *
4792 5062 * @return int 0 if the same sort or (+/-) to indicate which is greater.
4793 5063 */
4794 5064 public static function sort_modules( $a, $b ) {
4795 - if ( $a['sort'] === $b['sort'] ) {
4796 - return 0;
4797 - }
4798 -
4799 - return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5065 + return $a['sort'] <=> $b['sort'];
4800 5066 }
4801 5067
4802 5068 /**
4803 5069 * Recheck SSL status for use via an AJAX call.
@@ -4811,10 +5077,12 @@
4811 5077 $result = self::permit_ssl( true );
4812 5078 wp_send_json(
4813 5079 array(
4814 5080 'enabled' => $result,
4815 - 'message' => get_transient( 'jetpack_https_test_message' ),
4816 - )
5081 + 'message' => self::get_ssl_test_message(),
5082 + ),
5083 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
5084 + JSON_UNESCAPED_SLASHES
4817 5085 );
4818 5086 }
4819 5087
4820 5088 /* Client API */
@@ -4821,8 +5089,10 @@
4821 5089
4822 5090 /**
4823 5091 * Verify the onboarding token.
4824 5092 *
5093 + * @deprecated since 13.9
5094 + *
4825 5095 * @param array $token_data Token data.
4826 5096 * @param string $token Token value.
4827 5097 * @param string $request_data JSON-encoded request data.
4828 5098 *
@@ -4828,8 +5098,9 @@
4828 5098 *
4829 5099 * @return mixed
4830 5100 */
4831 5101 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
5102 + _deprecated_function( __METHOD__, '13.9' );
4832 5103 // Default to a blog token.
4833 5104 $token_type = 'blog';
4834 5105
4835 5106 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
@@ -4857,9 +5128,9 @@
4857 5128 $jp_user = get_user_by( 'email', $jpo_user );
4858 5129 if ( is_a( $jp_user, 'WP_User' ) ) {
4859 5130 wp_set_current_user( $jp_user->ID );
4860 5131 $user_can = is_multisite()
4861 - ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5132 + ? current_user_can_for_site( get_current_blog_id(), 'manage_options' )
4862 5133 : current_user_can( 'manage_options' );
4863 5134 if ( $user_can ) {
4864 5135 $token_type = 'user';
4865 5136 $token->external_user_id = $jp_user->ID;
@@ -4876,11 +5147,13 @@
4876 5147
4877 5148 /**
4878 5149 * Create a random secret for validating onboarding payload
4879 5150 *
5151 + * @deprecated since 13.9
4880 5152 * @return string Secret token
4881 5153 */
4882 5154 public static function create_onboarding_token() {
5155 + _deprecated_function( __METHOD__, '13.9' );
4883 5156 $token = Jetpack_Options::get_option( 'onboarding' );
4884 5157 if ( false === ( $token ) ) {
4885 5158 $token = wp_generate_password( 32, false );
4886 5159 Jetpack_Options::update_option( 'onboarding', $token );
@@ -4891,11 +5164,13 @@
4891 5164
4892 5165 /**
4893 5166 * Remove the onboarding token
4894 5167 *
5168 + * @deprecated since 13.9
4895 5169 * @return bool True on success, false on failure
4896 5170 */
4897 5171 public static function invalidate_onboarding_token() {
5172 + _deprecated_function( __METHOD__, '13.9' );
4898 5173 return Jetpack_Options::delete_option( 'onboarding' );
4899 5174 }
4900 5175
4901 5176 /**
@@ -4900,8 +5175,10 @@
4900 5175
4901 5176 /**
4902 5177 * Validate an onboarding token for a specific action
4903 5178 *
5179 + * @deprecated since 13.9
5180 + *
4904 5181 * @param string $token Onboarding token.
4905 5182 * @param string $action Action name.
4906 5183 *
4907 5184 * @return boolean True if token/action pair is accepted, false if not
@@ -4906,8 +5183,9 @@
4906 5183 *
4907 5184 * @return boolean True if token/action pair is accepted, false if not
4908 5185 */
4909 5186 public static function validate_onboarding_token_action( $token, $action ) {
5187 + _deprecated_function( __METHOD__, '13.9' );
4910 5188 // Compare tokens, bail if tokens do not match.
4911 5189 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4912 5190 return false;
4913 5191 }
@@ -4933,39 +5211,33 @@
4933 5211 * @return boolean
4934 5212 * @since 2.3.3
4935 5213 */
4936 5214 public static function permit_ssl( $force_recheck = false ) {
4937 - // Do some fancy tests to see if ssl is being supported.
4938 - if ( ! $force_recheck ) {
4939 - $ssl = get_transient( 'jetpack_https_test' );
4940 - }
5215 + // Delegates to the canonical SSL check in the Connection package.
5216 + return Heartbeat::permit_ssl( $force_recheck );
5217 + }
4941 5218
4942 - if ( $force_recheck || false === $ssl ) {
4943 - $message = '';
4944 - if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4945 - $ssl = 0;
4946 - } else {
4947 - $ssl = 1;
5219 + /**
5220 + * Returns a localized message describing the last SSL connectivity failure, if any.
5221 + *
5222 + * The Connection package's canonical SSL check stores a neutral reason code; this maps it to
5223 + * a translated, `jetpack`-domain message for display in the admin notice and AJAX recheck.
5224 + *
5225 + * @since 16.1
5226 + *
5227 + * @return string The localized message, or an empty string when there is no failure.
5228 + */
5229 + public static function get_ssl_test_message() {
5230 + $error = Heartbeat::get_ssl_test_error();
4948 5231
4949 - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4950 - $ssl = 0;
4951 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4952 - } else {
4953 - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4954 - if ( is_wp_error( $response ) ) {
4955 - $ssl = 0;
4956 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4957 - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4958 - $ssl = 0;
4959 - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4960 - }
4961 - }
4962 - }
4963 - set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4964 - set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5232 + switch ( $error['code'] ) {
5233 + case 'no_ssl_support':
5234 + return __( 'WordPress reports no SSL support', 'jetpack' );
5235 + case 'bad_response':
5236 + return __( 'Response was not OK: ', 'jetpack' ) . $error['detail'];
5237 + default:
5238 + return '';
4965 5239 }
4966 -
4967 - return (bool) $ssl;
4968 5240 }
4969 5241
4970 5242 /**
4971 5243 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
@@ -4984,9 +5256,9 @@
4984 5256 <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>
4985 5257 <p>
4986 5258 <?php esc_html_e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
4987 5259 <a href="#" id="jetpack-recheck-ssl-button"><?php esc_html_e( 'Try again', 'jetpack' ); ?></a>
4988 - <span id="jetpack-recheck-ssl-output"><?php echo esc_html( get_transient( 'jetpack_https_test_message' ) ); ?></span>
5260 + <span id="jetpack-recheck-ssl-output"><?php echo esc_html( self::get_ssl_test_message() ); ?></span>
4989 5261 </p>
4990 5262 <p>
4991 5263 <?php
4992 5264 printf(
@@ -5005,18 +5277,18 @@
5005 5277 <script type="text/javascript">
5006 5278 jQuery( document ).ready( function( $ ) {
5007 5279 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5008 5280 var $this = $( this );
5009 - $this.html( <?php echo wp_json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5281 + $this.html( <?php echo wp_json_encode( esc_html__( 'Checking', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5010 5282 $( '#jetpack-recheck-ssl-output' ).html( '' );
5011 5283 e.preventDefault();
5012 - var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce ); ?> };
5284 + var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> };
5013 5285 $.post( ajaxurl, data )
5014 5286 .done( function( response ) {
5015 5287 if ( response.enabled ) {
5016 5288 $( '#jetpack-ssl-warning' ).hide();
5017 5289 } else {
5018 - this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5290 + this.html( <?php echo wp_json_encode( esc_html__( 'Try again', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5019 5291 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5020 5292 }
5021 5293 }.bind( $this ) );
5022 5294 } );
@@ -5042,26 +5314,8 @@
5042 5314 return $jetpack->connection_manager;
5043 5315 }
5044 5316
5045 5317 /**
5046 - * Creates two secret tokens and the end of life timestamp for them.
5047 - *
5048 - * Note these tokens are unique per call, NOT static per site for connecting.
5049 - *
5050 - * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5051 - *
5052 - * @since 2.6
5053 - * @param String $action The action name.
5054 - * @param Integer $user_id The user identifier.
5055 - * @param Integer $exp Expiration time in seconds.
5056 - * @return array
5057 - */
5058 - public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5059 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5060 - return self::connection()->generate_secrets( $action, $user_id, $exp );
5061 - }
5062 -
5063 - /**
5064 5318 * Get verification secrets.
5065 5319 *
5066 5320 * @param string $action Action name.
5067 5321 * @param int $user_id User ID.
@@ -5082,35 +5336,8 @@
5082 5336 return $secrets;
5083 5337 }
5084 5338
5085 5339 /**
5086 - * Register a connection.
5087 - *
5088 - * @deprecated Jetpack 9.7.0
5089 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
5090 - *
5091 - * @return bool|WP_Error
5092 - */
5093 - public static function register() {
5094 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5095 - return static::connection()->try_registration( false );
5096 - }
5097 -
5098 - /**
5099 - * Filters the registration request body to include tracking properties.
5100 - *
5101 - * @deprecated Jetpack 9.7.0
5102 - * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5103 - *
5104 - * @param array $properties Token request properties.
5105 - * @return array amended properties.
5106 - */
5107 - public static function filter_register_request_body( $properties ) {
5108 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5109 - return Connection_Utils::filter_register_request_body( $properties );
5110 - }
5111 -
5112 - /**
5113 5340 * Filters the token request body to include tracking properties.
5114 5341 *
5115 5342 * @param array $properties Token request properties.
5116 5343 *
@@ -5131,9 +5358,9 @@
5131 5358
5132 5359 /**
5133 5360 * If the db version is showing something other that what we've got now, bump it to current.
5134 5361 *
5135 - * @return bool: True if the option was incorrect and updated, false if nothing happened.
5362 + * @return bool True if the option was incorrect and updated, false if nothing happened.
5136 5363 */
5137 5364 public static function maybe_set_version_option() {
5138 5365 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5139 5366 if ( JETPACK__VERSION !== $version ) {
@@ -5151,21 +5378,8 @@
5151 5378
5152 5379 /* Client Server API */
5153 5380
5154 5381 /**
5155 - * Loads the Jetpack XML-RPC client.
5156 - * No longer necessary, as the XML-RPC client will be automagically loaded.
5157 - *
5158 - * Note: we cannot remove this function yet as it is used in this plugin:
5159 - * https://wordpress.org/plugins/jetpack-subscription-form/
5160 - *
5161 - * @deprecated since 7.7.0
5162 - */
5163 - public static function load_xml_rpc_client() {
5164 - _deprecated_function( __METHOD__, 'jetpack-7.7' );
5165 - }
5166 -
5167 - /**
5168 5382 * State is passed via cookies from one request to the next, but never to subsequent requests.
5169 5383 * SET: state( $key, $value );
5170 5384 * GET: $value = state( $key );
5171 5385 *
@@ -5239,20 +5453,8 @@
5239 5453
5240 5454 self::state( 'privacy_checks', $privacy_checks );
5241 5455 }
5242 5456
5243 - /**
5244 - * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5245 - *
5246 - * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5247 - *
5248 - * @param string $url WordPress.com static resource URL.
5249 - */
5250 - public static function staticize_subdomain( $url ) {
5251 - _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5252 - return Assets::staticize_subdomain( $url );
5253 - }
5254 -
5255 5457 /* JSON API Authorization */
5256 5458
5257 5459 /**
5258 5460 * Handles the login action for Authorizing the JSON API
@@ -5257,13 +5459,14 @@
5257 5459 /**
5258 5460 * Handles the login action for Authorizing the JSON API
5259 5461 */
5260 5462 public function login_form_json_api_authorization() {
5261 - $this->verify_json_api_authorization_request();
5463 + $authorize_json_api = new Authorize_Json_Api();
5464 + $authorize_json_api->verify_json_api_authorization_request();
5262 5465
5263 - add_action( 'wp_login', array( $this, 'store_json_api_authorization_token' ), 10, 2 );
5466 + add_action( 'wp_login', array( $authorize_json_api, 'store_json_api_authorization_token' ), 10, 2 );
5264 5467
5265 - add_action( 'login_message', array( $this, 'login_message_json_api_authorization' ) );
5468 + add_action( 'login_message', array( $authorize_json_api, 'login_message_json_api_authorization' ) );
5266 5469 add_action( 'login_form', array( $this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5267 5470 add_filter( 'site_url', array( $this, 'post_login_form_to_signed_url' ), 10, 3 );
5268 5471 }
5269 5472
@@ -5301,16 +5504,17 @@
5301 5504
5302 5505 /**
5303 5506 * If someone logs in to approve API access, store the Access Code in usermeta.
5304 5507 *
5508 + * @deprecated 13.4
5509 + *
5305 5510 * @param string $user_login Unused.
5306 5511 * @param WP_User $user User logged in.
5307 5512 */
5308 5513 public function store_json_api_authorization_token( $user_login, $user ) {
5309 - add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5310 - add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_public_api_domain' ) );
5311 - $token = wp_generate_password( 32, false );
5312 - update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5514 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::store_json_api_authorization_token' );
5515 +
5516 + return ( new Authorize_Json_Api() )->store_json_api_authorization_token( $user_login, $user );
5313 5517 }
5314 5518
5315 5519 /**
5316 5520 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
@@ -5316,23 +5520,29 @@
5316 5520 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5317 5521 *
5318 5522 * To be used with a filter of allowed domains for a redirect.
5319 5523 *
5524 + * @deprecated 13.4
5525 + *
5320 5526 * @param array $domains Allowed WP.com Environments.
5321 5527 */
5322 5528 public function allow_wpcom_public_api_domain( $domains ) {
5323 - $domains[] = 'public-api.wordpress.com';
5324 - return $domains;
5529 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Status\\Host::allow_wpcom_public_api_domain' );
5530 +
5531 + return Host::allow_wpcom_public_api_domain( $domains );
5325 5532 }
5326 5533
5327 5534 /**
5328 5535 * Check if the redirect is encoded.
5329 5536 *
5537 + * @deprecated 13.4
5538 + *
5330 5539 * @param string $redirect_url Redirect URL.
5331 5540 *
5332 5541 * @return bool If redirect has been encoded.
5333 5542 */
5334 5543 public static function is_redirect_encoded( $redirect_url ) {
5544 + _deprecated_function( __METHOD__, 'jetpack-13.4' );
5335 5545 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5336 5546 }
5337 5547
5338 5548 /**
@@ -5350,8 +5560,10 @@
5350 5560
5351 5561 /**
5352 5562 * Add the Access Code details to the public-api.wordpress.com redirect.
5353 5563 *
5564 + * @deprecated 13.4
5565 + *
5354 5566 * @param string $redirect_to URL.
5355 5567 * @param string $original_redirect_to URL.
5356 5568 * @param WP_User $user WP_User for the redirect.
5357 5569 *
@@ -5357,23 +5569,18 @@
5357 5569 *
5358 5570 * @return string
5359 5571 */
5360 5572 public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5361 - return add_query_arg(
5362 - urlencode_deep(
5363 - array(
5364 - 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5365 - 'jetpack-user-id' => (int) $user->ID,
5366 - 'jetpack-state' => $this->json_api_authorization_request['state'],
5367 - )
5368 - ),
5369 - $redirect_to
5370 - );
5573 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::add_token_to_login_redirect_json_api_authorization' );
5574 +
5575 + return ( new Authorize_Json_Api() )->add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user );
5371 5576 }
5372 5577
5373 5578 /**
5374 5579 * Verifies the request by checking the signature
5375 5580 *
5581 + * @deprecated 13.4
5582 + *
5376 5583 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5377 5584 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5378 5585 *
5379 5586 * @param null|array $environment Value to override $_REQUEST.
@@ -5378,194 +5585,24 @@
5378 5585 *
5379 5586 * @param null|array $environment Value to override $_REQUEST.
5380 5587 */
5381 5588 public function verify_json_api_authorization_request( $environment = null ) {
5382 - $environment = $environment === null
5383 - ? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function.
5384 - : $environment;
5589 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::verify_json_api_authorization_request' );
5385 5590
5386 - list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
5387 - $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
5388 - if ( ! $token || empty( $token->secret ) ) {
5389 - wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5390 - }
5391 -
5392 - $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' );
5393 -
5394 - // Host has encoded the request URL, probably as a result of a bad http => https redirect.
5395 - 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.
5396 - /**
5397 - * Jetpack authorisation request Error.
5398 - *
5399 - * @since 7.5.0
5400 - */
5401 - do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5402 - $die_error = sprintf(
5403 - /* translators: %s is a URL */
5404 - __( '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' ),
5405 - Redirect::get_url( 'jetpack-support-double-encoding' )
5406 - );
5407 - }
5408 -
5409 - $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5410 -
5411 - if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5412 - $signature = $jetpack_signature->sign_request(
5413 - $environment['token'],
5414 - $environment['timestamp'],
5415 - $environment['nonce'],
5416 - '',
5417 - 'GET',
5418 - $environment['jetpack_json_api_original_query'],
5419 - null,
5420 - true
5421 - );
5422 - } else {
5423 - $signature = $jetpack_signature->sign_current_request(
5424 - array(
5425 - 'body' => null,
5426 - 'method' => 'GET',
5427 - )
5428 - );
5429 - }
5430 -
5431 - if ( ! $signature ) {
5432 - wp_die(
5433 - wp_kses(
5434 - $die_error,
5435 - array(
5436 - 'a' => array(
5437 - 'href' => array(),
5438 - ),
5439 - )
5440 - )
5441 - );
5442 - } elseif ( is_wp_error( $signature ) ) {
5443 - wp_die(
5444 - wp_kses(
5445 - $die_error,
5446 - array(
5447 - 'a' => array(
5448 - 'href' => array(),
5449 - ),
5450 - )
5451 - )
5452 - );
5453 - } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5454 - if ( is_ssl() ) {
5455 - // 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.
5456 - $signature = $jetpack_signature->sign_current_request(
5457 - array(
5458 - 'scheme' => 'http',
5459 - 'body' => null,
5460 - 'method' => 'GET',
5461 - )
5462 - );
5463 - if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5464 - wp_die(
5465 - wp_kses(
5466 - $die_error,
5467 - array(
5468 - 'a' => array(
5469 - 'href' => array(),
5470 - ),
5471 - )
5472 - )
5473 - );
5474 - }
5475 - } else {
5476 - wp_die(
5477 - wp_kses(
5478 - $die_error,
5479 - array(
5480 - 'a' => array(
5481 - 'href' => array(),
5482 - ),
5483 - )
5484 - )
5485 - );
5486 - }
5487 - }
5488 -
5489 - $timestamp = (int) $environment['timestamp'];
5490 - $nonce = stripslashes( (string) $environment['nonce'] );
5491 -
5492 - if ( ! $this->connection_manager ) {
5493 - $this->connection_manager = new Connection_Manager();
5494 - }
5495 -
5496 - if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
5497 - // De-nonce the nonce, at least for 5 minutes.
5498 - // 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).
5499 - $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5500 - if ( $old_nonce_time < time() - 300 ) {
5501 - wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
5502 - }
5503 - }
5504 -
5505 - $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
5506 - $data_filters = array(
5507 - 'state' => 'opaque',
5508 - 'client_id' => 'int',
5509 - 'client_title' => 'string',
5510 - 'client_image' => 'url',
5511 - );
5512 -
5513 - foreach ( $data_filters as $key => $sanitation ) {
5514 - if ( ! isset( $data->$key ) ) {
5515 - wp_die(
5516 - wp_kses(
5517 - $die_error,
5518 - array(
5519 - 'a' => array(
5520 - 'href' => array(),
5521 - ),
5522 - )
5523 - )
5524 - );
5525 - }
5526 -
5527 - switch ( $sanitation ) {
5528 - case 'int':
5529 - $this->json_api_authorization_request[ $key ] = (int) $data->$key;
5530 - break;
5531 - case 'opaque':
5532 - $this->json_api_authorization_request[ $key ] = (string) $data->$key;
5533 - break;
5534 - case 'string':
5535 - $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
5536 - break;
5537 - case 'url':
5538 - $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
5539 - break;
5540 - }
5541 - }
5542 -
5543 - if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5544 - wp_die(
5545 - wp_kses(
5546 - $die_error,
5547 - array(
5548 - 'a' => array(
5549 - 'href' => array(),
5550 - ),
5551 - )
5552 - )
5553 - );
5554 - }
5591 + return ( new Authorize_Json_Api() )->verify_json_api_authorization_request( $environment );
5555 5592 }
5556 5593
5557 5594 /**
5558 5595 * HTML for the JSON API authorization notice.
5559 5596 *
5597 + * @deprecated 13.4
5598 + *
5560 5599 * @return string
5561 5600 */
5562 5601 public function login_message_json_api_authorization() {
5563 - return '<p class="message">' . sprintf(
5564 - /* translators: Name/image of the client requesting authorization */
5565 - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
5566 - '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5567 - ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5602 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::login_message_json_api_authorization' );
5603 +
5604 + return ( new Authorize_Json_Api() )->login_message_json_api_authorization();
5568 5605 }
5569 5606
5570 5607 /**
5571 5608 * Get $content_width, but with a <s>twist</s> filter.
@@ -5609,32 +5646,23 @@
5609 5646
5610 5647 /**
5611 5648 * Checks if the site is currently in an identity crisis.
5612 5649 *
5650 + * Now delegates to the Connection package so this matches what the heartbeat itself reports.
5651 + * Note the package guards on `Connection\Manager::is_connected()` where this used to guard on
5652 + * `Jetpack::is_connection_ready()`, so the `jetpack_is_connection_ready` filter no longer applies.
5653 + *
5654 + * @deprecated 16.2
5655 + *
5613 5656 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
5614 5657 */
5615 5658 public static function check_identity_crisis() {
5616 - if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
5617 - return false;
5618 - }
5659 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Identity_Crisis::check_identity_crisis' );
5619 5660
5620 - return Jetpack_Options::get_option( 'sync_error_idc' );
5661 + return Identity_Crisis::check_identity_crisis();
5621 5662 }
5622 5663
5623 5664 /**
5624 - * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
5625 - *
5626 - * @since 4.4.0
5627 - * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
5628 - *
5629 - * @return bool
5630 - */
5631 - public static function validate_sync_error_idc_option() {
5632 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' );
5633 - return Identity_Crisis::validate_sync_error_idc_option();
5634 - }
5635 -
5636 - /**
5637 5665 * Normalizes a url by doing three things:
5638 5666 * - Strips protocol
5639 5667 * - Strips www
5640 5668 * - Adds a trailing slash
@@ -5655,35 +5683,8 @@
5655 5683 return $url;
5656 5684 }
5657 5685
5658 5686 /**
5659 - * Gets the value that is to be saved in the jetpack_sync_error_idc option.
5660 - *
5661 - * @since 4.4.0
5662 - * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
5663 - * @deprecated 9.8.0 Use \\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option
5664 - *
5665 - * @param array $response HTTP response.
5666 - * @return array Array of the local urls, wpcom urls, and error code
5667 - */
5668 - public static function get_sync_error_idc_option( $response = array() ) {
5669 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' );
5670 - return Identity_Crisis::get_sync_error_idc_option( $response );
5671 - }
5672 -
5673 - /**
5674 - * Returns the value of the jetpack_sync_idc_optin filter, or constant.
5675 - * If set to true, the site will be put into staging mode.
5676 - *
5677 - * @since 4.3.2
5678 - * @return bool
5679 - */
5680 - public static function sync_idc_optin() {
5681 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' );
5682 - return Identity_Crisis::sync_idc_optin();
5683 - }
5684 -
5685 - /**
5686 5687 * Maybe Use a .min.css stylesheet, maybe not.
5687 5688 *
5688 5689 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
5689 5690 *
@@ -5704,9 +5705,9 @@
5704 5705 // Strip out the abspath.
5705 5706 $base = dirname( plugin_basename( $plugin ) );
5706 5707
5707 5708 // Short out on non-Jetpack assets.
5708 - if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
5709 + if ( ! str_starts_with( $base, 'jetpack/' ) ) {
5709 5710 return $url;
5710 5711 }
5711 5712
5712 5713 // File name parsing.
@@ -5746,15 +5747,15 @@
5746 5747 *
5747 5748 * @return mixed
5748 5749 */
5749 5750 public static function set_suffix_on_min( $src, $handle ) {
5750 - if ( false === strpos( $src, '.min.css' ) ) {
5751 + if ( ! is_string( $src ) || ! str_contains( $src, '.min.css' ) ) {
5751 5752 return $src;
5752 5753 }
5753 5754
5754 5755 if ( ! empty( self::$min_assets ) ) {
5755 5756 foreach ( self::$min_assets as $file => $path ) {
5756 - if ( false !== strpos( $src, $file ) ) {
5757 + if ( str_contains( $src, $file ) ) {
5757 5758 wp_style_add_data( $handle, 'suffix', '.min' );
5758 5759 return $src;
5759 5760 }
5760 5761 }
@@ -5784,8 +5785,10 @@
5784 5785 *
5785 5786 * Data passed in with the $data parameter will be available in the
5786 5787 * template file as $data['value']
5787 5788 *
5789 + * @html-template-var array $data
5790 + *
5788 5791 * @param string $template - Template file to load.
5789 5792 * @param array $data - Any data to pass along to the template.
5790 5793 * @return boolean - If template file was found.
5791 5794 **/
@@ -5803,8 +5806,19 @@
5803 5806 return false;
5804 5807 }
5805 5808
5806 5809 /**
5810 + * Register Jetpack-specific tests on the connection package's health test suite.
5811 + *
5812 + * @param \Automattic\Jetpack\Connection\Connection_Health_Tests $connection_tests The test suite instance.
5813 + */
5814 + public function register_jetpack_connection_tests( $connection_tests ) {
5815 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger/class-jetpack-cxn-tests.php';
5816 + $jetpack_tests = new Jetpack_Cxn_Tests();
5817 + $jetpack_tests->register_tests_on( $connection_tests );
5818 + }
5819 +
5820 + /**
5807 5821 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
5808 5822 */
5809 5823 public function deprecated_hooks() {
5810 5824 $filter_deprecated_list = array(
@@ -5931,13 +5945,8 @@
5931 5945 'jetpack_cache_plans' => array(
5932 5946 'replacement' => null,
5933 5947 'version' => 'jetpack-6.1.0',
5934 5948 ),
5935 -
5936 - 'jetpack_lazy_images_skip_image_with_atttributes' => array(
5937 - 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
5938 - 'version' => 'jetpack-6.5.0',
5939 - ),
5940 5949 'jetpack_enable_site_verification' => array(
5941 5950 'replacement' => null,
5942 5951 'version' => 'jetpack-6.5.0',
5943 5952 ),
@@ -6021,8 +6030,22 @@
6021 6030 'jetpack_are_blogging_prompts_enabled' => array(
6022 6031 'replacement' => null,
6023 6032 'version' => 'jetpack-11.8.0',
6024 6033 ),
6034 + 'jetpack_subscriptions_modal_enabled' => array(
6035 + 'replacement' => null,
6036 + 'version' => 'jetpack-12.7.0',
6037 + ),
6038 + 'jetpack_pre_connection_prompt_helpers' => array(
6039 + 'replacement' => null,
6040 + 'version' => 'jetpack-13.2.0',
6041 + ),
6042 + 'jetpack_contact_form_use_package' => array(
6043 + 'replacement' => null,
6044 + 'version' => 'jetpack-13.4.0',
6045 + ),
6046 + // jetpack_implode_frontend_css has been removed, but is not listed here. The updated behavior is exactly the only use of the filter.
6047 + // We can reassess formally deprecating it here later; for now, it would be noise with no functional difference.
6025 6048 );
6026 6049
6027 6050 foreach ( $filter_deprecated_list as $tag => $args ) {
6028 6051 if ( has_filter( $tag ) ) {
@@ -6125,9 +6148,9 @@
6125 6148 foreach ( $matches as $match ) {
6126 6149 $url = trim( $match['path'], "'\" \t" );
6127 6150
6128 6151 // If this is a data url, we don't want to mess with it.
6129 - if ( 'data:' === substr( $url, 0, 5 ) ) {
6152 + if ( str_starts_with( $url, 'data:' ) ) {
6130 6153 continue;
6131 6154 }
6132 6155
6133 6156 // If this is an absolute or protocol-agnostic url,
@@ -6153,113 +6176,8 @@
6153 6176 return $css;
6154 6177 }
6155 6178
6156 6179 /**
6157 - * This methods removes all of the registered css files on the front end
6158 - * from Jetpack in favor of using a single file. In effect "imploding"
6159 - * all the files into one file.
6160 - *
6161 - * Pros:
6162 - * - Uses only ONE css asset connection instead of 15
6163 - * - Saves a minimum of 56k
6164 - * - Reduces server load
6165 - * - Reduces time to first painted byte
6166 - *
6167 - * Cons:
6168 - * - Loads css for ALL modules. However all selectors are prefixed so it
6169 - * should not cause any issues with themes.
6170 - * - Plugins/themes dequeuing styles no longer do anything. See
6171 - * jetpack_implode_frontend_css filter for a workaround
6172 - *
6173 - * For some situations developers may wish to disable css imploding and
6174 - * instead operate in legacy mode where each file loads seperately and
6175 - * can be edited individually or dequeued. This can be accomplished with
6176 - * the following line:
6177 - *
6178 - * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6179 - *
6180 - * @param bool $travis_test Is this a test run.
6181 - *
6182 - * @since 3.2
6183 - */
6184 - public function implode_frontend_css( $travis_test = false ) {
6185 - $do_implode = true;
6186 - if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6187 - $do_implode = false;
6188 - }
6189 -
6190 - // Do not implode CSS when the page loads via the AMP plugin.
6191 - if ( Jetpack_AMP_Support::is_amp_request() ) {
6192 - $do_implode = false;
6193 - }
6194 -
6195 - /**
6196 - * Allow CSS to be concatenated into a single jetpack.css file.
6197 - *
6198 - * @since 3.2.0
6199 - *
6200 - * @param bool $do_implode Should CSS be concatenated? Default to true.
6201 - */
6202 - $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6203 -
6204 - // Do not use the imploded file when default behavior was altered through the filter.
6205 - if ( ! $do_implode ) {
6206 - return;
6207 - }
6208 -
6209 - // We do not want to use the imploded file in dev mode, or if not connected.
6210 - if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6211 - if ( ! $travis_test ) {
6212 - return;
6213 - }
6214 - }
6215 -
6216 - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen.
6217 - if ( get_option( 'sharedaddy_disable_resources' ) ) {
6218 - return;
6219 - }
6220 -
6221 - /*
6222 - * Now we assume Jetpack is connected and able to serve the single
6223 - * file.
6224 - *
6225 - * In the future there will be a check here to serve the file locally
6226 - * or potentially from the Jetpack CDN
6227 - *
6228 - * For now:
6229 - * - Enqueue a single imploded css file
6230 - * - Zero out the style_loader_tag for the bundled ones
6231 - * - Be happy, drink scotch
6232 - */
6233 -
6234 - add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6235 -
6236 - $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6237 -
6238 - wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6239 - wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6240 - }
6241 -
6242 - /**
6243 - * Removes styles that are part of concatenated group.
6244 - *
6245 - * @param string $tag Style tag.
6246 - * @param string $handle Style handle.
6247 - *
6248 - * @return string
6249 - */
6250 - public function concat_remove_style_loader_tag( $tag, $handle ) {
6251 - if ( in_array( $handle, $this->concatenated_style_handles, true ) ) {
6252 - $tag = '';
6253 - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6254 - $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6255 - }
6256 - }
6257 -
6258 - return $tag;
6259 - }
6260 -
6261 - /**
6262 6180 * Check the heartbeat data
6263 6181 *
6264 6182 * Organizes the heartbeat data by severity. For example, if the site
6265 6183 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
@@ -6271,9 +6189,20 @@
6271 6189 *
6272 6190 * $return array $filtered_data
6273 6191 */
6274 6192 public static function jetpack_check_heartbeat_data() {
6275 - $raw_data = Jetpack_Heartbeat::generate_stats_array();
6193 + /*
6194 + * Site environment stats (incl. wp-version/php-version checked below) now live in the Connection package,
6195 + * and the IDC stat is contributed by the Connection package's `jetpack_heartbeat_stats_array` filter
6196 + * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6197 + * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6198 + * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6199 + */
6200 + $raw_data = array_merge(
6201 + Jetpack_Heartbeat::generate_stats_array(),
6202 + Heartbeat::get_environment_stats(),
6203 + array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6204 + );
6276 6205
6277 6206 $good = array();
6278 6207 $caution = array();
6279 6208 $bad = array();
@@ -6339,23 +6268,8 @@
6339 6268 return Jetpack_Options::get_options_for_reset();
6340 6269 }
6341 6270
6342 6271 /**
6343 - * Strip http:// or https:// from a url, replaces forward slash with ::,
6344 - * so we can bring them directly to their site in calypso.
6345 - *
6346 - * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6347 - *
6348 - * @param string $url URL.
6349 - * @return string url without the guff.
6350 - */
6351 - public static function build_raw_urls( $url ) {
6352 - _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6353 -
6354 - return ( new Status() )->get_site_suffix( $url );
6355 - }
6356 -
6357 - /**
6358 6272 * Get the user's meta box order.
6359 6273 *
6360 6274 * @param array $sorted Value for the user's option.
6361 6275 * @return mixed
@@ -6365,9 +6279,9 @@
6365 6279 return $sorted;
6366 6280 }
6367 6281
6368 6282 foreach ( $sorted as $box_context => $ids ) {
6369 - if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6283 + if ( ! str_contains( $ids, 'dashboard_stats' ) ) {
6370 6284 // If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6371 6285 continue;
6372 6286 }
6373 6287
@@ -6384,68 +6298,9 @@
6384 6298 }
6385 6299
6386 6300 return $sorted;
6387 6301 }
6388 -
6389 6302 /**
6390 - * Adds a "blank" column in the user admin table to display indication of user connection.
6391 - *
6392 - * @param array $columns User list table columns.
6393 - *
6394 - * @return array
6395 - */
6396 - public function jetpack_icon_user_connected( $columns ) {
6397 - $columns['user_jetpack'] = '';
6398 - return $columns;
6399 - }
6400 -
6401 - /**
6402 - * Show Jetpack icon if the user is linked.
6403 - *
6404 - * @param string $val HTML for the icon.
6405 - * @param string $col User list table column.
6406 - * @param int $user_id User ID.
6407 - *
6408 - * @return string
6409 - */
6410 - public function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
6411 - if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) {
6412 - $jetpack_logo = new Jetpack_Logo();
6413 - $emblem_html = sprintf(
6414 - '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
6415 - esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
6416 - $jetpack_logo->get_jp_emblem()
6417 - );
6418 - return $emblem_html;
6419 - }
6420 -
6421 - return $val;
6422 - }
6423 -
6424 - /**
6425 - * Style the Jetpack user column
6426 - */
6427 - public function jetpack_user_col_style() {
6428 - global $current_screen;
6429 - if ( ! empty( $current_screen->base ) && 'users' === $current_screen->base ) {
6430 - ?>
6431 - <style>
6432 - .fixed .column-user_jetpack {
6433 - width: 21px;
6434 - }
6435 - .jp-emblem-user-admin svg {
6436 - width: 20px;
6437 - height: 20px;
6438 - }
6439 - .jp-emblem-user-admin path {
6440 - fill: #069e08;
6441 - }
6442 - </style>
6443 - <?php
6444 - }
6445 - }
6446 -
6447 - /**
6448 6303 * Checks if Akismet is active and working.
6449 6304 *
6450 6305 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
6451 6306 * that implied usage of methods present since more recent version.
@@ -6537,26 +6392,15 @@
6537 6392 /**
6538 6393 * Return Calypso environment value; used for developing Jetpack and pairing
6539 6394 * it with different Calypso enrionments, such as localhost.
6540 6395 *
6541 - * @since 7.4.0
6396 + * @deprecated 12.4 Moved to the Status package.
6542 6397 *
6543 6398 * @return string Calypso environment
6544 6399 */
6545 6400 public static function get_calypso_env() {
6546 - if ( isset( $_GET['calypso_env'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6547 - return sanitize_key( $_GET['calypso_env'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6548 - }
6549 -
6550 - if ( getenv( 'CALYPSO_ENV' ) ) {
6551 - return sanitize_key( getenv( 'CALYPSO_ENV' ) );
6552 - }
6553 -
6554 - if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
6555 - return sanitize_key( CALYPSO_ENV );
6556 - }
6557 -
6558 - return '';
6401 + _deprecated_function( __METHOD__, 'jetpack-12.4', '\Automattic\Jetpack\Status\Host->get_calypso_env' );
6402 + return ( new Host() )->get_calypso_env();
6559 6403 }
6560 6404
6561 6405 /**
6562 6406 * Returns the hostname with protocol for Calypso.
@@ -6566,9 +6410,9 @@
6566 6410 *
6567 6411 * @return string Calypso host.
6568 6412 */
6569 6413 public static function get_calypso_host() {
6570 - $calypso_env = self::get_calypso_env();
6414 + $calypso_env = ( new Host() )->get_calypso_env();
6571 6415 switch ( $calypso_env ) {
6572 6416 case 'development':
6573 6417 return 'http://calypso.localhost:3000/';
6574 6418 case 'wpcalypso':
@@ -6617,13 +6461,20 @@
6617 6461 }
6618 6462 }
6619 6463
6620 6464 /**
6621 - * Returns a boolean for whether backups UI should be displayed or not.
6465 + * Whether UI for backups should be displayed.
6622 6466 *
6467 + * On WPCom platforms this is gated on the backups-self-serve site feature.
6468 + * On self-hosted Jetpack sites it falls back to the jetpack_show_backups filter.
6469 + *
6623 6470 * @return bool Should backups UI be displayed?
6624 6471 */
6625 6472 public static function show_backups_ui() {
6473 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6474 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'backups-self-serve' );
6475 + }
6476 +
6626 6477 /**
6627 6478 * Whether UI for backups should be displayed.
6628 6479 *
6629 6480 * @since 6.5.0
@@ -6633,8 +6484,24 @@
6633 6484 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
6634 6485 }
6635 6486
6636 6487 /**
6488 + * Whether UI for security scanning should be displayed.
6489 + *
6490 + * On WPCom platforms this is gated on the scan-self-serve site feature.
6491 + * On self-hosted Jetpack sites it always returns true.
6492 + *
6493 + * @return bool Should scan UI be displayed?
6494 + */
6495 + public static function show_scan_ui() {
6496 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6497 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'scan-self-serve' );
6498 + }
6499 +
6500 + return true;
6501 + }
6502 +
6503 + /**
6637 6504 * Clean leftoveruser meta.
6638 6505 *
6639 6506 * Delete Jetpack-related user meta when it is no longer needed.
6640 6507 *
@@ -6706,8 +6573,40 @@
6706 6573 'url' => Redirect::get_url( 'jetpack-faq-backup-disclaimer' ),
6707 6574 ),
6708 6575 );
6709 6576
6577 + $products['creator'] = array(
6578 + 'title' => __( 'Jetpack Creator', 'jetpack' ),
6579 + 'slug' => 'jetpack_creator_yearly',
6580 + 'description' => __( 'Craft stunning content, boost your subscriber base, and monetize your online presence.', 'jetpack' ),
6581 + 'show_promotion' => true,
6582 + 'discount_percent' => 50,
6583 + 'included_in_plans' => array( 'complete' ),
6584 + 'features' => array(
6585 + _x( 'Unlimited subscriber imports', 'Creator Product Feature', 'jetpack' ),
6586 + _x( 'Earn more from your content', 'Creator Product Feature', 'jetpack' ),
6587 + _x( 'Accept payments with PayPal', 'Creator Product Feature', 'jetpack' ),
6588 + _x( 'Increase earnings with WordAds', 'Creator Product Feature', 'jetpack' ),
6589 + ),
6590 + );
6591 +
6592 + $products['growth'] = array(
6593 + 'title' => __( 'Jetpack Growth', 'jetpack' ),
6594 + 'slug' => 'jetpack_growth_yearly',
6595 + 'description' => __( 'Essential tools to help you grow your audience, track visitor engagement, and turn leads into loyal customers and advocates.', 'jetpack' ),
6596 + 'show_promotion' => true,
6597 + 'discount_percent' => 50,
6598 + 'included_in_plans' => array( 'complete' ),
6599 + 'features' => array(
6600 + _x( 'Jetpack Social', 'Growth Product Feature', 'jetpack' ),
6601 + _x( 'Jetpack Stats (10K site views, upgradeable)', 'Growth Product Feature', 'jetpack' ),
6602 + _x( 'Unlimited subscriber imports', 'Growth Product Feature', 'jetpack' ),
6603 + _x( 'Earn more from your content', 'Growth Product Feature', 'jetpack' ),
6604 + _x( 'Accept payments with PayPal', 'Growth Product Feature', 'jetpack' ),
6605 + _x( 'Increase earnings with WordAds', 'Growth Product Feature', 'jetpack' ),
6606 + ),
6607 + );
6608 +
6710 6609 $products['scan'] = array(
6711 6610 'title' => __( 'Jetpack Scan', 'jetpack' ),
6712 6611 'slug' => 'jetpack_scan',
6713 6612 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ),
@@ -6737,9 +6636,9 @@
6737 6636 ),
6738 6637 );
6739 6638
6740 6639 $products['akismet'] = array(
6741 - 'title' => __( 'Jetpack Anti-Spam', 'jetpack' ),
6640 + 'title' => __( 'Akismet Anti-spam', 'jetpack' ),
6742 6641 'slug' => 'jetpack_anti_spam',
6743 6642 'description' => __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack' ),
6744 6643 'show_promotion' => true,
6745 6644 'discount_percent' => 50,
@@ -6745,9 +6644,8 @@
6745 6644 'discount_percent' => 50,
6746 6645 'included_in_plans' => array( 'security' ),
6747 6646 'features' => array(
6748 6647 _x( 'Comment and form spam protection', 'Anti-Spam Product Feature', 'jetpack' ),
6749 - _x( 'Powered by Akismet', 'Anti-Spam Product Feature', 'jetpack' ),
6750 6648 _x( 'Block spam without CAPTCHAs', 'Anti-Spam Product Feature', 'jetpack' ),
6751 6649 _x( 'Advanced stats', 'Anti-Spam Product Feature', 'jetpack' ),
6752 6650 ),
6753 6651 );
@@ -6834,5 +6732,106 @@
6834 6732
6835 6733 return true;
6836 6734 }
6837 6735
6736 + /**
6737 + * Add 5-star review link on the Jetpack Plugin meta, in the plugins list table (on the plugins page).
6738 + *
6739 + * @param array $plugin_meta An array of the plugin's metadata.
6740 + * @param string $plugin_file Path to the plugin file.
6741 + *
6742 + * @return array $plugin_meta An array of the plugin's metadata.
6743 + */
6744 + public function add_5_star_review_link( $plugin_meta, $plugin_file ) {
6745 + if ( $plugin_file !== 'jetpack/jetpack.php' ) {
6746 + return $plugin_meta;
6747 + }
6748 +
6749 + $u = get_current_user_id();
6750 + $site = get_site_url();
6751 +
6752 + $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">'
6753 + . str_repeat( '<span class="dashicons dashicons-star-filled" style="font-size: 16px; width:16px; height: 16px"></span>', 5 )
6754 + . '</a>';
6755 +
6756 + return $plugin_meta;
6757 + }
6758 +
6759 + /**
6760 + * Lazy instantiation of the Plugin_Tracking object.
6761 + *
6762 + * @since 13.9
6763 + *
6764 + * @return void
6765 + */
6766 + public function initialize_tracking() {
6767 + if ( did_action( 'jetpack_initialize_tracking' ) > 1 ) {
6768 + // Only need to run once.
6769 + return;
6770 + }
6771 +
6772 + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) || static::is_connection_ready() ) {
6773 + ( new Plugin_Tracking() )->init();
6774 + }
6775 + }
6776 +
6777 + /**
6778 + * Run the "initialize tracking" hook.
6779 + *
6780 + * @since 13.9
6781 + */
6782 + public function run_initialize_tracking_action() {
6783 + /**
6784 + * Fires when the tracking needs to be initialized.
6785 + * Doesn't necessarily mean that will actually happen, depends if the 'jetpack_tos_agreed' option is set.
6786 + *
6787 + * @since 13.9
6788 + */
6789 + do_action( 'jetpack_initialize_tracking' );
6790 + }
6791 +
6792 + /**
6793 + * Initialize REST jsonAPI if needed.
6794 + *
6795 + * @return void
6796 + */
6797 + public function maybe_initialize_rest_jsonapi() {
6798 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
6799 + if ( ! empty( $_GET['jsonapi'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) {
6800 + require_once ABSPATH . 'wp-admin/includes/admin.php'; // JSON API relies on WP functionality not autoloaded in REST.
6801 +
6802 + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
6803 + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
6804 + }
6805 + }
6806 +
6807 + /**
6808 + * Run plugin post-activation actions if we need to.
6809 + *
6810 + * @return void
6811 + */
6812 + private function plugin_post_activation() {
6813 + if ( ( new Status() )->is_offline_mode() ) {
6814 + return;
6815 + }
6816 +
6817 + if ( get_transient( 'activated_jetpack' ) ) {
6818 + delete_transient( 'activated_jetpack' );
6819 +
6820 + if ( ( new Host() )->is_woa_site() ) {
6821 + $redirect_url = static::admin_url( 'page=jetpack' );
6822 + } elseif ( is_network_admin() ) {
6823 + $redirect_url = admin_url( 'network/admin.php?page=jetpack' );
6824 + } elseif ( get_transient( 'my_jetpack_product_activated' ) ) {
6825 + // don't redirect if this is an activation that just came from My Jetpack
6826 + // My Jetpack has its own set of post-activation redirects
6827 + return;
6828 + } elseif ( My_Jetpack_Initializer::should_initialize() ) {
6829 + $redirect_url = static::admin_url( 'page=my-jetpack' );
6830 + } else {
6831 + $redirect_url = static::admin_url( 'page=jetpack' );
6832 + }
6833 +
6834 + wp_safe_redirect( $redirect_url );
6835 + }
6836 + }
6838 6837 }