PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
← All changes | class.jetpack.php +888 -989 13.2.4 → 16.3-a.5 View file →
@@ -7,31 +7,38 @@
7 7 * @package automattic/jetpack
8 8 */
9 9
10 10 use Automattic\Jetpack\Assets;
11 -use Automattic\Jetpack\Boost_Speed_Score\Jetpack_Boost_Modules;
12 11 use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
13 12 use Automattic\Jetpack\Config;
13 +use Automattic\Jetpack\Connection\Authorize_Json_Api;
14 14 use Automattic\Jetpack\Connection\Client;
15 15 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
16 -use Automattic\Jetpack\Connection\Nonce_Handler;
17 16 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
18 17 use Automattic\Jetpack\Connection\Secrets;
19 18 use Automattic\Jetpack\Connection\Tokens;
20 -use Automattic\Jetpack\Connection\Utils as Connection_Utils;
19 +use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
21 20 use Automattic\Jetpack\Constants;
22 21 use Automattic\Jetpack\CookieState;
23 22 use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
24 23 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
25 24 use Automattic\Jetpack\Errors;
25 +use Automattic\Jetpack\Feature_Policy;
26 26 use Automattic\Jetpack\Files;
27 +use Automattic\Jetpack\Heartbeat;
27 28 use Automattic\Jetpack\Identity_Crisis;
29 +use Automattic\Jetpack\Import\Main as Import_Main;
28 30 use Automattic\Jetpack\Licensing;
29 31 use Automattic\Jetpack\Modules;
30 32 use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
33 +use Automattic\Jetpack\Newsletter\Reader_Link;
31 34 use Automattic\Jetpack\Paths;
35 +use Automattic\Jetpack\Plugin\Deprecate;
32 36 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
37 +use Automattic\Jetpack\Podcast\Podcast;
33 38 use Automattic\Jetpack\Redirect;
39 +use Automattic\Jetpack\Scan_Page\Jetpack_Scan as Scan_Page_Init;
40 +use Automattic\Jetpack\SEO\Initializer as Jetpack_SEO_Initializer;
34 41 use Automattic\Jetpack\Status;
35 42 use Automattic\Jetpack\Status\Host;
36 43 use Automattic\Jetpack\Status\Visitor;
37 44 use Automattic\Jetpack\Sync\Actions as Sync_Actions;
@@ -38,9 +45,14 @@
38 45 use Automattic\Jetpack\Sync\Health;
39 46 use Automattic\Jetpack\Sync\Sender;
40 47 use Automattic\Jetpack\Terms_Of_Service;
41 48 use Automattic\Jetpack\Tracking;
49 +use Automattic\Woocommerce_Analytics;
42 50
51 +if ( ! defined( 'ABSPATH' ) ) {
52 + exit( 0 );
53 +}
54 +
43 55 /*
44 56 Options:
45 57 jetpack_options (array)
46 58 An array of options.
@@ -75,74 +87,8 @@
75 87 */
76 88 public $xmlrpc_server = null;
77 89
78 90 /**
79 - * List of Jetpack modules that have CSS that gets concatenated into jetpack.css.
80 - *
81 - * See $concatenated_style_handles for the list of handles,
82 - * and the implode_frontend_css method for more details.
83 - *
84 - * When updating this list, make sure to update $concatenated_style_handles as well.
85 - *
86 - * @var array List of Jetpack modules.
87 - */
88 - public $modules_with_concatenated_css = array(
89 - 'carousel',
90 - 'contact-form',
91 - 'infinite-scroll',
92 - 'likes',
93 - 'related-posts',
94 - 'sharedaddy',
95 - 'shortcodes',
96 - 'subscriptions',
97 - 'tiled-gallery',
98 - 'widgets',
99 - );
100 -
101 - /**
102 - * The handles of styles that are concatenated into jetpack.css.
103 - *
104 - * When making changes to that list,
105 - * you must also update concat_list in tools/webpack.config.css.js,
106 - * and to $modules_with_concatenated_css if necessary.
107 - *
108 - * @var array The handles of styles that are concatenated into jetpack.css.
109 - */
110 - public $concatenated_style_handles = array(
111 - 'jetpack-carousel-swiper-css',
112 - 'jetpack-carousel',
113 - 'grunion.css',
114 - 'the-neverending-homepage',
115 - 'jetpack_likes',
116 - 'jetpack_related-posts',
117 - 'sharedaddy',
118 - 'jetpack-slideshow',
119 - 'presentations',
120 - 'quiz',
121 - 'jetpack-subscriptions',
122 - 'jetpack-responsive-videos',
123 - 'jetpack-social-menu',
124 - 'tiled-gallery',
125 - 'jetpack_display_posts_widget',
126 - 'gravatar-profile-widget',
127 - 'goodreads-widget',
128 - 'jetpack_social_media_icons_widget',
129 - 'jetpack-top-posts-widget',
130 - 'jetpack_image_widget',
131 - 'jetpack-my-community-widget',
132 - 'jetpack-authors-widget',
133 - 'wordads',
134 - 'eu-cookie-law-style',
135 - 'flickr-widget-style',
136 - 'jetpack-search-widget',
137 - 'jetpack-simple-payments-widget-style',
138 - 'jetpack-widget-social-icons-styles',
139 - 'wpcom_instagram_widget',
140 - 'milestone-widget',
141 - 'subscribe-modal-css',
142 - );
143 -
144 - /**
145 91 * Contains all assets that have had their URL rewritten to minified versions.
146 92 *
147 93 * @var array
148 94 */
@@ -157,11 +103,8 @@
157 103 'contact-form' => array(
158 104 array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
159 105 array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
160 106 ),
161 - 'custom-css' => array(
162 - array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
163 - ),
164 107 'gravatar-hovercards' => array(
165 108 array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
166 109 ),
167 110 'latex' => array(
@@ -332,9 +275,8 @@
332 275 * Graph tags via filter when their Social Meta modules are active:
333 276 *
334 277 * - All in One SEO Pack, All in one SEO Pack Pro
335 278 * - WordPress SEO by Yoast, WordPress SEO Premium by Yoast
336 - * - SEOPress, SEOPress Pro
337 279 *
338 280 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
339 281 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
340 282 *
@@ -377,8 +319,10 @@
377 319 'wp-caregiver/wp-caregiver.php', // WP Caregiver.
378 320 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta.
379 321 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
380 322 'wp-ogp/wp-ogp.php', // WP-OGP.
323 + 'wp-seopress/seopress.php', // SEOPress.
324 + 'wp-seopress-pro/seopress-pro.php', // SEOPress Pro.
381 325 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
382 326 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button.
383 327 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox.
384 328 'seo-by-rank-math/rank-math.php', // Rank Math.
@@ -445,8 +389,10 @@
445 389
446 390 /**
447 391 * Verified data for JSON authorization request
448 392 *
393 + * @deprecated 13.4
394 + *
449 395 * @var array
450 396 */
451 397 public $json_api_authorization_request = array();
452 398
@@ -487,8 +433,16 @@
487 433 */
488 434 public static $instance = false;
489 435
490 436 /**
437 + * Resolved answer for `is_premium_analytics_enabled()`, or null before the first call.
438 + *
439 + * @since 16.1
440 + * @var bool|null
441 + */
442 + private static $premium_analytics_enabled = null;
443 +
444 + /**
491 445 * Singleton
492 446 *
493 447 * @static
494 448 */
@@ -528,9 +482,9 @@
528 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
529 483 self::update_active_modules( $modules );
530 484 }
531 485
532 - add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
486 + self::register_upgrade_init_hooks();
533 487
534 488 // Upgrade to 4.3.0.
535 489 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
536 490 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
@@ -585,8 +539,16 @@
585 539 Jetpack_Options::delete_option( 'autoupdate_plugins' );
586 540 } // Should we have some type of fallback if something fails here?
587 541 }
588 542
543 + // Set the newsletter send default option for existing sites.
544 + if ( false === get_option( 'wpcom_newsletter_send_default' ) ) {
545 + add_option( 'wpcom_newsletter_send_default', 1 );
546 + }
547 +
548 + // Its handler went with the Recommendations assistant.
549 + wp_clear_scheduled_hook( 'jetpack_recommend_videopress' );
550 +
589 551 if ( did_action( 'wp_loaded' ) ) {
590 552 self::upgrade_on_load();
591 553 } else {
592 554 add_action(
@@ -620,9 +582,8 @@
620 582 }
621 583
622 584 if (
623 585 class_exists( 'Jetpack_Sitemap_Manager' )
624 - && version_compare( JETPACK__VERSION, '5.3', '>=' )
625 586 ) {
626 587 do_action( 'jetpack_sitemaps_purge_data' );
627 588 }
628 589
@@ -637,9 +598,9 @@
637 598 * Also fires Action hooks for each newly activated and deactivated module.
638 599 *
639 600 * @param array $modules Array of active modules to be saved in options.
640 601 *
641 - * @return $success bool true for success, false for failure.
602 + * @return bool $success true for success, false for failure.
642 603 */
643 604 public static function update_active_modules( $modules ) {
644 605 return ( new Modules() )->update_active( $modules );
645 606 }
@@ -693,41 +654,17 @@
693 654 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
694 655 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
695 656 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
696 657
697 - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
698 -
699 - add_filter(
700 - 'jetpack_signature_check_token',
701 - array( __CLASS__, 'verify_onboarding_token' ),
702 - 10,
703 - 3
704 - );
705 -
706 658 /**
707 659 * Prepare Gutenberg Editor functionality
660 + *
661 + * The hooks previously here have been moved to modules/blocks.php but leaving this here pending
662 + * a longer investigation to see if code is expecting the Gutenberg class to always be available.
708 663 */
709 664 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
710 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
711 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 );
712 - /**
713 - * We've switched from enqueue_block_editor_assets to enqueue_block_assets in WP-Admin because the assets with the former are loaded on the main site-editor.php.
714 - *
715 - * With the latter, the assets are now loaded in the SE iframe; the implementation is now faster because Gutenberg doesn't need to inject the assets in the iframe on client-side.
716 - */
717 - if ( is_admin() ) {
718 - add_action( 'enqueue_block_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
719 - } else {
720 - add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
721 - }
722 - add_filter( 'render_block', array( 'Jetpack_Gutenberg', 'display_deprecated_block_message' ), 10, 2 );
723 -
724 665 add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
725 666
726 - // Unlink user before deleting the user from WP.com.
727 - add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 );
728 - add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 );
729 -
730 667 add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
731 668
732 669 add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
733 670 add_action( 'login_init', array( $this, 'login_init' ) );
@@ -734,8 +671,13 @@
734 671
735 672 // Set up the REST authentication hooks.
736 673 Connection_Rest_Authentication::init();
737 674
675 + // Register Jetpack-specific connection tests (sync health, etc.) with the connection
676 + // package's health test suite. This runs on all requests (not just admin), because
677 + // the connection/test REST endpoint can be called outside admin context.
678 + add_action( 'jetpack_connection_tests_loaded', array( $this, 'register_jetpack_connection_tests' ) );
679 +
738 680 add_action( 'admin_init', array( $this, 'admin_init' ) );
739 681 add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
740 682
741 683 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
@@ -768,29 +710,8 @@
768 710
769 711 add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
770 712 add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
771 713
772 - require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php';
773 - $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() );
774 - add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) );
775 -
776 - /*
777 - * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
778 - * We should make sure to only do this for front end links.
779 - */
780 - if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
781 - add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
782 - add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
783 -
784 - /*
785 - * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
786 - * so they point moderation links on emails to Calypso.
787 - */
788 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/functions.wp-notify.php';
789 - add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
790 - add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
791 - }
792 -
793 714 add_action(
794 715 'plugins_loaded',
795 716 function () {
796 717 if ( User_Agent_Info::is_mobile_app() ) {
@@ -801,18 +722,10 @@
801 722
802 723 // Update the site's Jetpack plan and products from API on heartbeats.
803 724 add_action( 'jetpack_heartbeat', array( Jetpack_Plan::class, 'refresh_from_wpcom' ) );
804 725
805 - /**
806 - * This is the hack to concatenate all css files into one.
807 - * For description and reasoning see the implode_frontend_css method.
808 - *
809 - * Super late priority so we catch all the registered styles.
810 - */
811 - if ( ! is_admin() ) {
812 - add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first.
813 - add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`.
814 - }
726 + // The Connection package fetches the site record for `jetpack/v4/site`; reuse it to refresh the plan.
727 + add_action( 'jetpack_site_data_fetched', array( Jetpack_Plan::class, 'update_from_site_record' ) );
815 728
816 729 // Actually push the stats on shutdown.
817 730 if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
818 731 add_action( 'shutdown', array( $this, 'push_stats' ) );
@@ -820,8 +733,10 @@
820 733
821 734 // After a successful connection.
822 735 add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) );
823 736 add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) );
737 + add_action( 'jetpack_site_registered', array( Reader_Link::class, 'activate_on_connection' ), 9 );
738 + add_action( 'jetpack_site_registered', array( \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::class, 'discard_credentials' ) );
824 739
825 740 // Actions for Manager::authorize().
826 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
827 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
@@ -826,8 +741,9 @@
826 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
827 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
828 743 add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
829 744
745 + Jetpack_Client_Server::init();
830 746 add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) );
831 747 add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) );
832 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
833 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
@@ -832,9 +748,9 @@
832 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
833 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
834 750
835 751 // Filters for the Manager::get_token() urls and request body.
836 - add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
752 + add_filter( 'jetpack_token_redirect_url', array( Authorize_Redirect::class, 'filter_connect_redirect_url' ) );
837 753 add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
838 754
839 755 // Filter for the `jetpack/v4/connection/data` API response.
840 756 add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) );
@@ -860,16 +776,142 @@
860 776
861 777 // Register product descriptions for partner coupon usage.
862 778 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
863 779
864 - // Actions for conditional recommendations.
865 - add_action( 'plugins_loaded', array( 'Jetpack_Recommendations', 'init_conditional_recommendation_actions' ) );
866 -
867 780 // Add 5-star
868 781 add_filter( 'plugin_row_meta', array( $this, 'add_5_star_review_link' ), 10, 2 );
782 + add_action( 'init', array( Deprecate::class, 'instance' ) );
783 +
784 + // Register Jetpack module management abilities (WordPress Abilities API, WP 6.9+).
785 + \Automattic\Jetpack\Plugin\Abilities\Modules_Abilities::init();
786 +
787 + // Register Connection abilities (WordPress Abilities API, WP 6.9+). Scoped to the
788 + // Jetpack plugin for now: the Connection package no longer auto-wires these, so
789 + // connection-only consumers (Boost, Protect, Search, etc.) do not register them yet.
790 + \Automattic\Jetpack\Connection\Abilities\Connection_Abilities::init();
869 791 }
870 792
871 793 /**
794 + * Whether the current request should eagerly initialize the admin/REST-only
795 + * packages (the Import package and My Jetpack) now, at `plugins_loaded` time.
796 + *
797 + * Returns true for admin, cron, POST, and WP-CLI requests — the contexts,
798 + * knowable this early, where those packages have work to do. Returns false
799 + * for a plain front-end GET *and* for a REST request: the two can't be told
800 + * apart yet (this runs before `rest_api_init`), so callers defer the REST
801 + * case by initializing the package on `rest_api_init` instead, while a plain
802 + * page view never fires that hook and so loads nothing. This keeps
803 + * admin/REST-only PHP out of opcache on the front-end GET hot path.
804 + *
805 + * No in-repo code depends on the deferral. The one externally observable
806 + * change is timing: the packages' documented init hooks
807 + * (`jetpack_import_initialized`, `jetpack_feature_import_enabled`, and
808 + * `my_jetpack_init`) no longer fire on a plain front-end GET — they fire on
809 + * the admin, cron, POST, WP-CLI, and REST requests where the packages load.
810 + *
811 + * @return bool
812 + */
813 + private static function should_eager_load_packages() {
814 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) );
815 + $is_wp_cli = Constants::is_true( 'WP_CLI' );
816 +
817 + return is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli;
818 + }
819 +
820 + /**
821 + * Configure the Import package from a deferred hook.
822 + *
823 + * The eager path uses Config::ensure( 'import' ), but the deferred REST path
824 + * runs after Config::on_plugins_loaded() has already processed its feature
825 + * flags, so it needs a hookable bootstrap callback. Preserve Config's
826 + * feature-enabled action for hook consumers.
827 + *
828 + * @since 16.0
829 + *
830 + * @return void
831 + */
832 + public static function configure_import_package() {
833 + if ( class_exists( Import_Main::class ) ) {
834 + Import_Main::configure();
835 +
836 + if ( ! did_action( 'jetpack_feature_import_enabled' ) ) {
837 + do_action( 'jetpack_feature_import_enabled' );
838 + }
839 + }
840 + }
841 +
842 + /**
843 + * Whether the bundled Stats v2 dashboard is enabled.
844 + *
845 + * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
846 + * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
847 + * menu alongside the existing Stats UI; it never replaces or hides the
848 + * legacy Stats menu, admin-bar entries, post-list column, or WP dashboard
849 + * widget. The Stats module's tracking is unaffected either way, and Stats v2
850 + * reads what that module collects, so while the module is off the plugin
851 + * answers false here before even reading the flag.
852 + *
853 + * The package has to be loadable for this to be true, so a site with the
854 + * flag on but a missing package answers false here and never adds the
855 + * Stats v2 menu (a warning is logged instead).
856 + *
857 + * @since 16.1
858 + *
859 + * @return bool
860 + */
861 + public static function is_premium_analytics_enabled() {
862 + if ( null !== self::$premium_analytics_enabled ) {
863 + return self::$premium_analytics_enabled;
864 + }
865 +
866 + if ( ! self::is_module_active( 'stats' ) ) {
867 + self::$premium_analytics_enabled = false;
868 + return false;
869 + }
870 +
871 + /**
872 + * Filters whether the bundled Premium Analytics dashboard is enabled.
873 + *
874 + * Resolved once, from `Jetpack::configure()` on `plugins_loaded`, and only
875 + * while the Stats module is active. Register this from a mu-plugin or a
876 + * plugin's main file — a callback added on `plugins_loaded` or later runs
877 + * too late to be seen.
878 + *
879 + * @since 16.1
880 + *
881 + * @param bool $enabled Defaults to the `jetpack_premium_analytics_enabled` option (false).
882 + */
883 + $flag = (bool) apply_filters( 'jetpack_premium_analytics_enabled', (bool) get_option( 'jetpack_premium_analytics_enabled' ) );
884 +
885 + self::$premium_analytics_enabled = $flag && class_exists( 'Automattic\Jetpack\PremiumAnalytics\Analytics' );
886 +
887 + if ( $flag && ! self::$premium_analytics_enabled ) {
888 + wp_trigger_error(
889 + __METHOD__,
890 + 'The jetpack_premium_analytics_enabled flag is on but the Premium Analytics package is not loadable; keeping the Stats UI in place.'
891 + );
892 + }
893 +
894 + return self::$premium_analytics_enabled;
895 + }
896 +
897 + /**
898 + * Expose the setting that turns the Premium Analytics dashboard on and off.
899 + *
900 + * Deliberately not behind is_premium_analytics_enabled(): this is the setting that flips that
901 + * check, so it has to answer while the dashboard is still off.
902 + *
903 + * @since 16.2
904 + *
905 + * @return void
906 + */
907 + public static function register_premium_analytics_enablement_setting() {
908 + if ( class_exists( 'Automattic\Jetpack\PremiumAnalytics\Enablement_Setting' ) ) {
909 + \Automattic\Jetpack\PremiumAnalytics\Enablement_Setting::register();
910 + }
911 + }
912 +
913 + /**
872 914 * Before everything else starts getting initalized, we need to initialize Jetpack using the
873 915 * Config object.
874 916 */
875 917 public function configure() {
@@ -878,13 +920,10 @@
878 920 foreach (
879 921 array(
880 922 'jitm',
881 923 'sync',
924 + 'account_protection',
882 925 'waf',
883 - 'videopress',
884 - 'stats',
885 - 'stats_admin',
886 - 'import',
887 926 )
888 927 as $feature
889 928 ) {
890 929 $config->ensure( $feature );
@@ -889,8 +928,103 @@
889 928 ) {
890 929 $config->ensure( $feature );
891 930 }
892 931
932 + // Enable the VideoPress admin UI (the "Jetpack > VideoPress" dashboard) inside the
933 + // Jetpack plugin, mirroring the standalone Jetpack VideoPress plugin. The dashboard
934 + // only renders when the VideoPress module is active (Status::is_active()); when it
935 + // is not, the menu item links to the My Jetpack interstitial to activate it.
936 + $config->ensure( 'videopress', array( 'admin_ui' => true ) );
937 +
938 + /*
939 + * The Import package only registers `jetpack/v4/import` REST routes — it
940 + * does nothing when rendering a front-end page — so gate its `ensure()`
941 + * to keep its PHP out of opcache on the front-end GET hot path. It still
942 + * loads on admin, cron, POST, and WP-CLI requests, and on `rest_api_init`
943 + * for REST: a REST request can't be identified yet at `plugins_loaded`
944 + * (this runs before `Config::on_plugins_loaded`, and `rest_api_init`
945 + * fires later), so it is initialized directly when that hook fires, while
946 + * a plain page view never fires it and so loads nothing.
947 + *
948 + * JITM stays eager (above): unlike Import, its `register()` adds a
949 + * `jetpack_sync_before_send_updated_option` filter that records the
950 + * `jetpack_last_plugin_sync` transient, and a Jetpack Sync send can fire
951 + * on a plain front-end GET — including the dedicated-sync `spawn-sync`
952 + * GET, which runs on `init` and exits before `rest_api_init`. Deferring
953 + * JITM would skip that bookkeeping and leave its message cache stale after
954 + * a plugin change, so it loads on every request as before.
955 + */
956 + if ( self::should_eager_load_packages() ) {
957 + $config->ensure( 'import' );
958 + } else {
959 + add_action(
960 + 'rest_api_init',
961 + array( __CLASS__, 'configure_import_package' ),
962 + 0
963 + );
964 + }
965 +
966 + /*
967 + * The Stats and Stats Admin packages only do work when the Stats module
968 + * is active (the front-end tracking pixel) or on wp-admin, REST, cron,
969 + * POST, and WP-CLI requests: the Stats dashboard page, the stats /
970 + * stats-app REST endpoints (which the block editor also calls for
971 + * email-open rates), the transient-cleanup cron, the connection
972 + * package's package-version tracker (which runs on POSTs and reads the
973 + * `jetpack_package_versions` filter that Stats registers), and CLI
974 + * introspection such as the heartbeat inspector. On a plain front-end
975 + * GET page view with the module off they are inert: the pixel
976 + * short-circuits on `Stats\Main::should_track()` and every other entry
977 + * point only hooks rest_api_init, admin, cron, the POST-only tracker, or
978 + * is reached through WP-CLI.
979 + * Skip loading them — and eagerly constructing the Stats Admin REST
980 + * controller — on that hot path to keep their PHP out of opcache, but
981 + * keep loading them everywhere else exactly as before so the stats REST
982 + * permission mapping (view_stats, registered by Stats\Main), the
983 + * editor's stats-app calls, the cleanup cron, and the package-version
984 + * tracker are all unchanged.
985 + *
986 + * REST requests are not yet identifiable here (REST_REQUEST is defined
987 + * after plugins_loaded), so defer those to rest_api_init. Call the
988 + * package initializers directly rather than `$config->ensure()`: ensure()
989 + * only flags a feature for `Config::on_plugins_loaded()` (plugins_loaded
990 + * priority 2), which has already run by the time rest_api_init fires.
991 + * Priority 0 runs before each package's own priority-10 route
992 + * registration, so their routes still register within the same dispatch.
993 + * A plain page view never fires rest_api_init, so the packages stay
994 + * unloaded there. See JETPACK-1747.
995 + */
996 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'];
997 + $is_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
998 +
999 + if ( self::is_module_active( 'stats' ) || is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli ) {
1000 + $config->ensure( 'stats' );
1001 + $config->ensure( 'stats_admin' );
1002 + } else {
1003 + add_action(
1004 + 'rest_api_init',
1005 + static function () {
1006 + if ( class_exists( 'Automattic\Jetpack\Stats\Main' ) ) {
1007 + \Automattic\Jetpack\Stats\Main::init();
1008 + }
1009 + if ( class_exists( 'Automattic\Jetpack\Stats_Admin\Main' ) ) {
1010 + \Automattic\Jetpack\Stats_Admin\Main::init();
1011 + }
1012 + },
1013 + 0
1014 + );
1015 + }
1016 +
1017 + // Stats v2 (WOOA7S-1595). Unlike Stats above it cannot be deferred when enabled — see
1018 + // Analytics::init() for why, and for why it takes no menu_title here.
1019 + if ( self::is_premium_analytics_enabled() ) {
1020 + \Automattic\Jetpack\PremiumAnalytics\Analytics::init();
1021 + }
1022 +
1023 + // Outside the check above on purpose — see Enablement_Setting. Deferred like Stats, to keep
1024 + // the autoload off the front-end hot path.
1025 + add_action( 'rest_api_init', array( __CLASS__, 'register_premium_analytics_enablement_setting' ), 0 );
1026 +
893 1027 $config->ensure(
894 1028 'connection',
895 1029 array(
896 1030 'slug' => 'jetpack',
@@ -908,12 +1042,8 @@
908 1042 );
909 1043
910 1044 $config->ensure( 'search' );
911 1045
912 - if ( defined( 'ENABLE_WORDADS_SHARED_UI' ) && ENABLE_WORDADS_SHARED_UI ) {
913 - $config->ensure( 'wordads' );
914 - }
915 -
916 1046 if ( ! $this->connection_manager ) {
917 1047 $this->connection_manager = new Connection_Manager( 'jetpack' );
918 1048 }
919 1049
@@ -921,8 +1051,10 @@
921 1051 if ( $modules->is_active( 'publicize' ) && $this->connection_manager->has_connected_user() ) {
922 1052 $config->ensure( 'publicize' );
923 1053 }
924 1054
1055 + add_action( 'jetpack_initialize_tracking', array( $this, 'initialize_tracking' ) );
1056 +
925 1057 /*
926 1058 * Load things that should only be in Network Admin.
927 1059 *
928 1060 * For now blow away everything else until a more full
@@ -937,8 +1069,9 @@
937 1069 $is_connection_ready = self::is_connection_ready();
938 1070
939 1071 if ( $is_connection_ready ) {
940 1072 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
1073 + $this->run_initialize_tracking_action();
941 1074
942 1075 Jetpack_Heartbeat::init();
943 1076 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
944 1077 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
@@ -943,8 +1076,19 @@
943 1076 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
944 1077 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
945 1078 Jetpack_Search_Performance_Logger::init();
946 1079 }
1080 + } else {
1081 + add_action( 'jetpack_agreed_to_terms_of_service', array( $this, 'run_initialize_tracking_action' ) );
1082 + add_action( 'rest_api_init', array( $this, 'run_initialize_tracking_action' ) );
1083 + add_filter(
1084 + 'xmlrpc_methods',
1085 + function ( $methods ) {
1086 + $this->run_initialize_tracking_action();
1087 + return $methods;
1088 + },
1089 + 1
1090 + );
947 1091 }
948 1092
949 1093 // Initialize remote file upload request handlers.
950 1094 $this->add_remote_request_handlers();
@@ -954,20 +1098,10 @@
954 1098 */
955 1099 if ( $is_connection_ready ) {
956 1100 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
957 1101 add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
958 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
959 - add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
1102 + add_action( 'rest_api_init', array( $this, 'maybe_initialize_rest_jsonapi' ) );
960 1103 }
961 -
962 - if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
963 - add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
964 - } else {
965 - /**
966 - * Initialize tracking right after the user agrees to the terms of service.
967 - */
968 - add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
969 - }
970 1104 }
971 1105
972 1106 /**
973 1107 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
@@ -975,16 +1109,58 @@
975 1109 *
976 1110 * @action plugins_loaded
977 1111 */
978 1112 public function late_initialization() {
979 - add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
1113 + add_action( 'after_setup_theme', array( 'Jetpack', 'load_modules' ), -2 );
980 1114
981 - My_Jetpack_Initializer::init();
1115 + /*
1116 + * My Jetpack is a wp-admin dashboard. Its Initializer::init() only wires
1117 + * up admin-menu, admin_init, and rest_api_init surfaces — and eagerly
1118 + * loads every product class (backup, boost, protect, …) just to register
1119 + * admin plugin-action links — so none of it is needed on a plain
1120 + * front-end GET page view. (The pieces that do immediate work, e.g.
1121 + * Connection REST authentication and Licensing, are already initialized
1122 + * unconditionally in Jetpack's constructor, so they are unaffected here.)
1123 + *
1124 + * Gate the call to the request types where My Jetpack actually does work.
1125 + * REST can't be detected yet at plugins_loaded, so initialize on
1126 + * rest_api_init for that branch; a plain page view never fires it, so My
1127 + * Jetpack stays unloaded there.
1128 + */
1129 + if ( self::should_eager_load_packages() ) {
1130 + My_Jetpack_Initializer::init();
1131 + } else {
1132 + add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1133 + }
982 1134
983 - // Initialize Boost Speed Score
984 - $modules = Jetpack_Boost_Modules::init();
985 - new Speed_Score( $modules, 'jetpack-dashboard' );
1135 + Scan_Page_Init::initialize();
1136 + Jetpack_SEO_Initializer::init();
986 1137
1138 + if ( ( new Modules() )->is_active( 'podcast' ) ) {
1139 + Podcast::init();
1140 + }
1141 +
1142 + /*
1143 + * Initialize Boost Speed Score. It only does work on REST requests (the
1144 + * dashboard speed-score endpoints) and on a few Jetpack Boost lifecycle
1145 + * actions, so defer constructing it — and loading the boost-speed-score
1146 + * package classes — until one of those hooks actually fires instead of on
1147 + * every request. Priority 0 ensures the object's own callbacks (added in
1148 + * its constructor at the default priority) still run for the firing hook.
1149 + */
1150 + $initialize_speed_score = static function () {
1151 + static $initialized = false;
1152 + if ( $initialized ) {
1153 + return;
1154 + }
1155 + $initialized = true;
1156 + new Speed_Score( array(), 'jetpack-dashboard' );
1157 + };
1158 + add_action( 'rest_api_init', $initialize_speed_score, 0 );
1159 + add_action( 'jetpack_boost_deactivate', $initialize_speed_score, 0 );
1160 + add_action( 'jetpack_boost_environment_changed', $initialize_speed_score, 0 );
1161 + add_action( 'handle_environment_change', $initialize_speed_score, 0 );
1162 +
987 1163 /**
988 1164 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
989 1165 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
990 1166 *
@@ -1022,8 +1198,10 @@
1022 1198
1023 1199 /**
1024 1200 * Redirect edit post links to Calypso.
1025 1201 *
1202 + * @deprecated since 13.9
1203 + *
1026 1204 * @param string $default_url Post edit URL.
1027 1205 * @param int $post_id Post ID.
1028 1206 *
1029 1207 * @return string
@@ -1028,8 +1206,10 @@
1028 1206 *
1029 1207 * @return string
1030 1208 */
1031 1209 public function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1210 + _deprecated_function( __METHOD__, '13.9' );
1211 +
1032 1212 $post = get_post( $post_id );
1033 1213
1034 1214 if ( empty( $post ) ) {
1035 1215 return $default_url;
@@ -1060,13 +1240,17 @@
1060 1240
1061 1241 /**
1062 1242 * Redirect edit comment links to Calypso.
1063 1243 *
1244 + * @deprecated since 13.9
1245 + *
1064 1246 * @param string $url Comment edit URL.
1065 1247 *
1066 1248 * @return string
1067 1249 */
1068 1250 public function point_edit_comment_links_to_calypso( $url ) {
1251 + _deprecated_function( __METHOD__, '13.9' );
1252 +
1069 1253 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1070 1254 $query_args = null;
1071 1255 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1072 1256
@@ -1085,30 +1269,16 @@
1085 1269 *
1086 1270 * @return array list of callables.
1087 1271 */
1088 1272 public function filter_sync_callable_whitelist( $callables ) {
1089 -
1090 1273 // Jetpack Functions.
1091 1274 $jetpack_callables = array(
1092 1275 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1093 1276 'updates' => array( 'Jetpack', 'get_updates' ),
1094 1277 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1278 + 'theme_styles' => array( 'Automattic\\Jetpack\\Plugin\\Theme_Styles_Sync', 'get_theme_styles' ),
1095 1279 );
1096 - $callables = array_merge( $callables, $jetpack_callables );
1097 -
1098 - // Jetpack_SSO_Helpers.
1099 - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1100 - $sso_helpers = array(
1101 - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1102 - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1103 - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1104 - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1105 - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1106 - );
1107 - $callables = array_merge( $callables, $sso_helpers );
1108 - }
1109 -
1110 - return $callables;
1280 + return array_merge( $callables, $jetpack_callables );
1111 1281 }
1112 1282
1113 1283 /**
1114 1284 * Extend Sync multisite callables with Jetpack Plugin functions.
@@ -1133,21 +1303,8 @@
1133 1303 return $callables;
1134 1304 }
1135 1305
1136 1306 /**
1137 - * Deprecated
1138 - * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1139 - *
1140 - * @param array $params The action parameters.
1141 - *
1142 - * @deprecated since 9.8.
1143 - */
1144 - public function jetpack_track_last_sync_callback( $params ) {
1145 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1146 - return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1147 - }
1148 -
1149 - /**
1150 1307 * If there are any stats that need to be pushed, but haven't been, push them now.
1151 1308 */
1152 1309 public function push_stats() {
1153 1310 if ( ! empty( $this->stats ) ) {
@@ -1162,16 +1319,8 @@
1162 1319 * @param string $cap Capability name.
1163 1320 */
1164 1321 public function jetpack_custom_caps( $caps, $cap ) {
1165 1322 switch ( $cap ) {
1166 - case 'jetpack_manage_modules':
1167 - case 'jetpack_activate_modules':
1168 - case 'jetpack_deactivate_modules':
1169 - $caps = array( 'manage_options' );
1170 - break;
1171 - case 'jetpack_configure_modules':
1172 - $caps = array( 'manage_options' );
1173 - break;
1174 1323 case 'jetpack_manage_autoupdates':
1175 1324 $caps = array(
1176 1325 'manage_options',
1177 1326 'update_plugins',
@@ -1189,9 +1338,9 @@
1189 1338 if ( $is_offline_mode ) {
1190 1339 $caps = array( 'manage_options' );
1191 1340 break;
1192 1341 } else {
1193 - $caps = array( 'read' );
1342 + $caps = array( 'edit_posts' );
1194 1343 }
1195 1344 break;
1196 1345 }
1197 1346 return $caps;
@@ -1350,9 +1499,9 @@
1350 1499 }
1351 1500 /**
1352 1501 * Does the network allow admins to add new users.
1353 1502 *
1354 - * @return boolian
1503 + * @return bool
1355 1504 */
1356 1505 public static function network_add_new_users() {
1357 1506 return (bool) get_site_option( 'add_new_users' );
1358 1507 }
@@ -1359,9 +1508,9 @@
1359 1508 /**
1360 1509 * File upload psace left per site in MB.
1361 1510 * -1 means NO LIMIT.
1362 1511 *
1363 - * @return number
1512 + * @return int
1364 1513 */
1365 1514 public static function network_site_upload_space() {
1366 1515 // value in MB.
1367 1516 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
@@ -1378,9 +1527,9 @@
1378 1527
1379 1528 /**
1380 1529 * Maximum file upload size set by the network.
1381 1530 *
1382 - * @return number
1531 + * @return int
1383 1532 */
1384 1533 public static function network_max_upload_file_size() {
1385 1534 // value in KB.
1386 1535 return get_site_option( 'fileupload_maxk', 300 );
@@ -1634,44 +1783,8 @@
1634 1783 return apply_filters( 'jetpack_is_connection_ready', self::connection()->is_connected(), self::connection() );
1635 1784 }
1636 1785
1637 1786 /**
1638 - * Deprecated: Is Jetpack in development (offline) mode?
1639 - *
1640 - * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1641 - * and themes still use it, and we do not want to flood them with notices.
1642 - *
1643 - * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1644 - *
1645 - * @deprecated since 8.0.
1646 - */
1647 - public static function is_development_mode() {
1648 - _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1649 - return ( new Status() )->is_offline_mode();
1650 - }
1651 -
1652 - /**
1653 - * Whether the site is currently onboarding or not.
1654 - * A site is considered as being onboarded if it currently has an onboarding token.
1655 - *
1656 - * @since 5.8
1657 - * @deprecated Use \Automattic\Jetpack\Status()->is_onboarding()
1658 - *
1659 - * @access public
1660 - * @static
1661 - *
1662 - * @return bool True if the site is currently onboarding, false otherwise
1663 - */
1664 - public static function is_onboarding() {
1665 - _deprecated_function( __METHOD__, 'jetpack-10.9', 'Automattic\\Jetpack\\Status\\is_onboarding' );
1666 -
1667 - if ( ! method_exists( 'Automattic\Jetpack\Status', 'is_onboarding' ) ) {
1668 - return Jetpack_Options::get_option( 'onboarding' ) !== false;
1669 - }
1670 - return ( new Status() )->is_onboarding();
1671 - }
1672 -
1673 - /**
1674 1787 * Determines reason for Jetpack offline mode.
1675 1788 */
1676 1789 public static function development_mode_trigger_text() {
1677 1790 $status = new Status();
@@ -1685,11 +1798,10 @@
1685 1798 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1686 1799 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1687 1800 } elseif ( $status->is_local_site() ) {
1688 1801 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1689 - /** This filter is documented in packages/status/src/class-status.php */
1690 - } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1691 - $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1802 + } elseif ( get_option( 'jetpack_offline_mode' ) ) {
1803 + $notice = __( 'The jetpack_offline_mode option is set to true.', 'jetpack' );
1692 1804 } else {
1693 1805 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1694 1806 }
1695 1807
@@ -1719,15 +1831,8 @@
1719 1831 $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), esc_url( Redirect::get_url( 'jetpack-contact-support-beta-group' ) ) );
1720 1832
1721 1833 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1722 1834 }
1723 - // Throw up a notice if using staging mode.
1724 - if ( ( new Status() )->is_staging_site() ) {
1725 - /* translators: %s is a URL */
1726 - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), esc_url( Redirect::get_url( 'jetpack-support-staging-sites' ) ) );
1727 -
1728 - echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1729 - }
1730 1835 }
1731 1836
1732 1837 /**
1733 1838 * Whether Jetpack's version maps to a public release, or a development version.
@@ -1748,28 +1853,8 @@
1748 1853 );
1749 1854 }
1750 1855
1751 1856 /**
1752 - * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1753 - *
1754 - * @param int $user_id User ID or will use get_current_user_id if false/not provided.
1755 - */
1756 - public static function is_user_connected( $user_id = false ) {
1757 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1758 - return self::connection()->is_user_connected( $user_id );
1759 - }
1760 -
1761 - /**
1762 - * Get the wpcom user data of the current|specified connected user.
1763 - *
1764 - * @param null|int $user_id User ID or will use get_current_user_id if null.
1765 - */
1766 - public static function get_connected_user_data( $user_id = null ) {
1767 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1768 - return self::connection()->get_connected_user_data( $user_id );
1769 - }
1770 -
1771 - /**
1772 1857 * Get the wpcom email of the current|specified connected user.
1773 1858 *
1774 1859 * @param null|int $user_id User ID or will use get_current_user_id if null.
1775 1860 */
@@ -1821,18 +1906,11 @@
1821 1906 */
1822 1907 public static function load_modules() {
1823 1908 $status = new Status();
1824 1909
1825 - if ( method_exists( $status, 'is_onboarding' ) ) {
1826 - $is_onboarding = $status->is_onboarding();
1827 - } else {
1828 - $is_onboarding = self::is_onboarding();
1829 - }
1830 -
1831 1910 if (
1832 1911 ! self::is_connection_ready()
1833 1912 && ! $status->is_offline_mode()
1834 - && ! $is_onboarding
1835 1913 && (
1836 1914 ! is_multisite()
1837 1915 || ! get_site_option( 'jetpack_protect_active' )
1838 1916 )
@@ -1953,22 +2031,14 @@
1953 2031 *
1954 2032 * @todo Store the result in core's object cache maybe?
1955 2033 */
1956 2034 public static function get_active_plugins() {
1957 - $active_plugins = (array) get_option( 'active_plugins', array() );
1958 -
1959 - if ( is_multisite() ) {
1960 - // Due to legacy code, active_sitewide_plugins stores them in the keys,
1961 - // whereas active_plugins stores them in the values.
1962 - $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1963 - if ( $network_plugins ) {
1964 - $active_plugins = array_merge( $active_plugins, $network_plugins );
1965 - }
2035 + // Older Connection copies can load first and lack this method.
2036 + if ( ! method_exists( Heartbeat::class, 'get_active_plugins' ) ) {
2037 + return array();
1966 2038 }
1967 2039
1968 - sort( $active_plugins );
1969 -
1970 - return array_unique( $active_plugins );
2040 + return Heartbeat::get_active_plugins();
1971 2041 }
1972 2042
1973 2043 /**
1974 2044 * Gets and parses additional plugin data to send with the heartbeat data
@@ -2106,8 +2176,10 @@
2106 2176 *
2107 2177 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2108 2178 */
2109 2179 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2180 + // @todo Remove this require once the deprecated Jetpack_Twitter_Cards wrapper has been removed.
2181 + // Twitter Cards functionality now lives in the jetpack-post-media package (Automattic\Jetpack\Post_Media\Twitter_Cards).
2110 2182 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2111 2183 }
2112 2184 }
2113 2185
@@ -2210,9 +2282,9 @@
2210 2282 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2211 2283 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2212 2284 }
2213 2285 wp_safe_redirect( self::admin_url( 'page=' . rawurlencode( $page ) ) );
2214 - exit;
2286 + exit( 0 );
2215 2287 }
2216 2288 }
2217 2289
2218 2290 /**
@@ -2259,8 +2331,12 @@
2259 2331 default:
2260 2332 break;
2261 2333 }
2262 2334 }
2335 + if ( method_exists( Feature_Policy::class, 'ensure_hooks' ) ) {
2336 + Feature_Policy::ensure_hooks();
2337 + }
2338 +
2263 2339 /**
2264 2340 * Filters the array of default modules.
2265 2341 *
2266 2342 * @since 2.5.0
@@ -2292,13 +2368,14 @@
2292 2368 * @return array
2293 2369 */
2294 2370 public function handle_deprecated_modules( $modules ) {
2295 2371 $deprecated_modules = array(
2296 - 'debug' => null, // Closed out and moved to the debugger library.
2297 - 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2298 - 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2299 - 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2300 - 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2372 + 'debug' => null, // Closed out and moved to the debugger library.
2373 + 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2374 + 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2375 + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2376 + 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2377 + 'enhanced-distribution' => null, // Closed out in 13.3 -- WP.com is winding down the firehose.
2301 2378 );
2302 2379
2303 2380 // Don't activate SSO if they never completed activating WPCC.
2304 2381 if ( self::is_module_active( 'wpcc' ) ) {
@@ -2352,8 +2429,17 @@
2352 2429 }
2353 2430 }
2354 2431 }
2355 2432
2433 + // Special case to convert block setting to a block module.
2434 + $block_key = array_search( 'blocks', $modules, true );
2435 + if ( $block_key !== false ) { // Only care if 'blocks' made it through the previous filters.
2436 + $block_option = get_option( 'jetpack_blocks_disabled', null );
2437 + if ( $block_option ) {
2438 + unset( $modules[ $block_key ] );
2439 + }
2440 + }
2441 +
2356 2442 return $modules;
2357 2443 }
2358 2444
2359 2445 /**
@@ -2410,23 +2496,30 @@
2410 2496
2411 2497 /**
2412 2498 * Return module name translation. Uses matching string created in modules/module-headings.php.
2413 2499 *
2500 + * The module list is globbed from `modules/` at runtime, so a module can be listed with no
2501 + * entry in that generated file. Fall back to the untranslated header rather than overwriting
2502 + * it with the null `jetpack_get_module_i18n()` returns for an unknown slug.
2503 + *
2414 2504 * @since 3.9.2
2415 2505 *
2416 2506 * @param array $modules Array of Jetpack modules.
2417 2507 *
2418 - * @return string|void
2508 + * @return array
2419 2509 */
2420 2510 public static function get_translated_modules( $modules ) {
2421 2511 foreach ( $modules as $index => $module ) {
2422 2512 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2423 - if ( isset( $module['name'] ) ) {
2424 - $modules[ $index ]['name'] = $i18n_module['name'];
2513 + $name = $i18n_module['name'] ?? null;
2514 + $description = $i18n_module['description'] ?? null;
2515 +
2516 + if ( null !== $name && isset( $module['name'] ) ) {
2517 + $modules[ $index ]['name'] = $name;
2425 2518 }
2426 - if ( isset( $module['description'] ) ) {
2427 - $modules[ $index ]['description'] = $i18n_module['description'];
2428 - $modules[ $index ]['short_description'] = $i18n_module['description'];
2519 + if ( null !== $description && isset( $module['description'] ) ) {
2520 + $modules[ $index ]['description'] = $description;
2521 + $modules[ $index ]['short_description'] = $description;
2429 2522 }
2430 2523 if ( isset( $module['module_tags'] ) ) {
2431 2524 $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2432 2525 }
@@ -2466,20 +2559,28 @@
2466 2559
2467 2560 /**
2468 2561 * Catches PHP errors. Must be used in conjunction with output buffering.
2469 2562 *
2563 + * @deprecated since 13.5
2470 2564 * @param bool $catch True to start catching, False to stop.
2471 2565 *
2472 2566 * @static
2567 + * @deprecated 13.5
2568 + * @see \Automattic\Jetpack\Errors
2473 2569 */
2474 2570 public static function catch_errors( $catch ) {
2571 + _deprecated_function( __METHOD__, '13.5' );
2572 + // @phan-suppress-next-line PhanDeprecatedClass
2475 2573 return ( new Errors() )->catch_errors( $catch );
2476 2574 }
2477 2575
2478 2576 /**
2479 2577 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2578 + *
2579 + * @deprecated since 13.5
2480 2580 */
2481 2581 public static function catch_errors_on_shutdown() {
2582 + _deprecated_function( __METHOD__, '13.5' );
2482 2583 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2483 2584 }
2484 2585
2485 2586 /**
@@ -2583,9 +2684,9 @@
2583 2684 ),
2584 2685 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2585 2686 );
2586 2687 wp_safe_redirect( $url );
2587 - exit;
2688 + exit( 0 );
2588 2689 }
2589 2690 }
2590 2691
2591 2692 /**
@@ -2603,9 +2704,8 @@
2603 2704
2604 2705 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating.
2605 2706 if ( $send_state_messages ) {
2606 2707 self::restate();
2607 - self::catch_errors( true );
2608 2708 }
2609 2709
2610 2710 $active = self::get_active_modules();
2611 2711
@@ -2673,10 +2773,8 @@
2673 2773 if ( $send_state_messages ) {
2674 2774 self::state( 'error', false );
2675 2775 self::state( 'module', false );
2676 2776 }
2677 -
2678 - self::catch_errors( false );
2679 2777 /**
2680 2778 * Fires when default modules are activated.
2681 2779 *
2682 2780 * @since 1.9.0
@@ -2734,9 +2832,9 @@
2734 2832 * @return string $url module configuration URL.
2735 2833 */
2736 2834 public static function module_configuration_url( $module ) {
2737 2835 $module = self::get_module_slug( $module );
2738 - $default_url = self::admin_url() . "#/settings?term=$module";
2836 + $default_url = self::admin_url( array( 'page' => 'jetpack-settings' ) ) . "#/settings?term=$module";
2739 2837 /**
2740 2838 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
2741 2839 *
2742 2840 * @since 6.9.0
@@ -2754,9 +2852,9 @@
2754 2852 *
2755 2853 * @param string $message Error message.
2756 2854 * @param bool $deactivate Deactivate Jetpack or not.
2757 2855 *
2758 - * @return void
2856 + * @return never
2759 2857 */
2760 2858 public static function bail_on_activation( $message, $deactivate = true ) {
2761 2859 ?>
2762 2860 <!doctype html>
@@ -2794,9 +2892,9 @@
2794 2892 if ( $update ) {
2795 2893 update_option( 'active_plugins', array_filter( $plugins ) );
2796 2894 }
2797 2895 }
2798 - exit;
2896 + exit( 0 );
2799 2897 }
2800 2898
2801 2899 /**
2802 2900 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
@@ -2821,12 +2919,18 @@
2821 2919 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
2822 2920
2823 2921 Health::on_jetpack_activated();
2824 2922
2923 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
2924 +
2825 2925 if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) {
2826 2926 Sync_Actions::do_only_first_initial_sync();
2827 2927 }
2828 2928
2929 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
2930 + Woocommerce_Analytics::maybe_add_proxy_speed_module();
2931 + }
2932 +
2829 2933 self::plugin_initialize();
2830 2934 }
2831 2935
2832 2936 /**
@@ -2861,9 +2965,9 @@
2861 2965
2862 2966 if ( $plugins_path === $referer['path'] ) {
2863 2967 $source_type = 'list';
2864 2968 } elseif ( $plugins_install_path === $referer['path'] ) {
2865 - $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
2969 + $tab = $query_parts['tab'] ?? 'featured';
2866 2970 switch ( $tab ) {
2867 2971 case 'popular':
2868 2972 $source_type = 'popular';
2869 2973 break;
@@ -2873,10 +2977,10 @@
2873 2977 case 'favorites':
2874 2978 $source_type = 'favorites';
2875 2979 break;
2876 2980 case 'search':
2877 - $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
2878 - $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
2981 + $source_type = 'search-' . ( $query_parts['type'] ?? 'term' );
2982 + $source_query = $query_parts['s'] ?? null;
2879 2983 break;
2880 2984 default:
2881 2985 $source_type = 'featured';
2882 2986 }
@@ -2885,29 +2989,171 @@
2885 2989 return array( $source_type, $source_query );
2886 2990 }
2887 2991
2888 2992 /**
2889 - * Runs before bumping version numbers up to a new version
2993 + * Runs before bumping version numbers up to a new version.
2890 2994 *
2891 - * @param string $version Version:timestamp.
2995 + * Only ever registered the hooks for the release post update modal, which has been removed.
2996 + * No longer hooked to `updating_jetpack_version`.
2997 + *
2998 + * @deprecated 16.2
2999 + *
3000 + * @param string $version Version:timestamp.
2892 3001 * @param string $old_version Old Version:timestamp or false if not set yet.
2893 3002 */
2894 - public static function do_version_bump( $version, $old_version ) {
2895 - if ( $old_version ) { // For existing Jetpack installations.
2896 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3003 + public static function do_version_bump( $version, $old_version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Signature preserved for the deprecation shim.
3004 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
3005 + }
2897 3006
2898 - // If a front end page is visited after the update, the 'wp' action will fire.
2899 - add_action( 'wp', 'Jetpack::set_update_modal_display' );
3007 + /**
3008 + * Enables the Newsletter (subscriptions) module for existing sites now that it is a default-on module.
3009 + *
3010 + * Fresh installs receive the module via its "Auto Activate: Yes" header, so this only handles sites
3011 + * upgrading from a version where the module defaulted off. It runs once per site (guarded by the
3012 + * subscriptions_default_on_migrated option). Fresh installs are marked as migrated immediately so the
3013 + * migration never runs for them. After it has run, the user's choice to deactivate the module again
3014 + * (for example from the My Jetpack Products page) is respected and never reverted.
3015 + *
3016 + * The module requires a connection, so on a disconnected site the migration is deferred without setting
3017 + * the guard, allowing a later version bump to retry once the site is connected.
3018 + *
3019 + * @param string $version New Jetpack version:timestamp.
3020 + * @param string|false $old_version Previous Jetpack version:timestamp, or false on a fresh install.
3021 + */
3022 + public static function activate_subscriptions_module_for_existing_sites( $version, $old_version ) {
3023 + if ( get_option( 'jetpack_subscriptions_default_on_migrated' ) ) {
3024 + return;
3025 + }
2900 3026
2901 - // If an admin page is visited after the update, the 'current_screen' action will fire.
2902 - add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3027 + // Fresh installs get the module via its "Auto Activate: Yes" header. Mark them as migrated so a
3028 + // later opt-out is never reverted by the existing-site path on a subsequent version bump.
3029 + if ( ! $old_version ) {
3030 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3031 + return;
2903 3032 }
3033 +
3034 + if ( ! self::is_connection_ready() ) {
3035 + return;
3036 + }
3037 +
3038 + // Mark as migrated only once the module is active, so a transient activation failure is retried on
3039 + // a later version bump rather than being silently skipped.
3040 + if ( self::is_module_active( 'subscriptions' ) || self::activate_module( 'subscriptions', false, false ) ) {
3041 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3042 + }
2904 3043 }
2905 3044
2906 3045 /**
3046 + * Option flag that records the AI master-switch opt-out reconciliation has run,
3047 + * so it never runs twice.
3048 + *
3049 + * @var string
3050 + */
3051 + const AI_MASTER_OPTOUT_MIGRATED_OPTION = 'jetpack_ai_master_optout_migrated';
3052 +
3053 + /**
3054 + * Register the on-upgrade init hooks whose relative ORDER matters, extracted so
3055 + * the ordering can be asserted in tests without invoking plugin_upgrade() (whose
3056 + * guards make it unreliable to trigger under test). activate_new_modules()
3057 + * (init, default priority 10) auto-activates "Auto Activate: Yes" modules
3058 + * including the `ai` master; reconcile_ai_master_optout() must run at a LATER
3059 + * priority to honor an explicit AI opt-out.
3060 + *
3061 + * @return void
3062 + */
3063 + public static function register_upgrade_init_hooks() {
3064 + add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
3065 + add_action( 'init', array( __CLASS__, 'reconcile_ai_master_optout' ), 20 );
3066 + }
3067 +
3068 + /**
3069 + * Preserves an explicit Jetpack AI opt-out when the `ai` module becomes the site-wide
3070 + * master switch off WordPress.com Simple (self-hosted and Atomic).
3071 + *
3072 + * The `ai` module is "Auto Activate: Yes", so on upgrade {@see self::activate_new_modules()}
3073 + * turns it on for connected sites — the desired default-on / auto-enable-on-connection
3074 + * behavior, which this method deliberately leaves alone. The one case it corrects is a site
3075 + * that had explicitly disabled Jetpack AI (the `jetpack_ai_enabled` option present and falsey)
3076 + * before the module shipped: that opt-out must survive the module becoming the master, so the
3077 + * module is deactivated for exactly those sites. An absent or truthy option is left untouched.
3078 + *
3079 + * Ordering is the whole point. `activate_new_modules()` is hooked on `init` at priority 10 and
3080 + * auto-activates the module there; this method is hooked on `init` at priority 20 (see
3081 + * {@see self::plugin_upgrade()}), so it runs AFTER the auto-activation and its deactivation is
3082 + * the final state. A version-guarded block that ran inline during `plugins_loaded` would be
3083 + * undone by the later auto-activation, which is why this is a late-init hook rather than an
3084 + * inline upgrade step.
3085 + *
3086 + * WordPress.com Simple never runs modules — the option stays the master there — so this is a
3087 + * no-op on Simple. The {@see self::AI_MASTER_OPTOUT_MIGRATED_OPTION} flag makes it run exactly
3088 + * once, which matters because off-Simple the option is no longer the master after this runs:
3089 + * a stale falsey option must not keep re-deactivating a module the user later turns back on.
3090 + *
3091 + * @return void
3092 + */
3093 + public static function reconcile_ai_master_optout() {
3094 + if ( get_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION ) ) {
3095 + return;
3096 + }
3097 +
3098 + // Simple keeps the `jetpack_ai_enabled` option as the master; modules don't run there.
3099 + if ( ( new Host() )->is_wpcom_simple() ) {
3100 + return;
3101 + }
3102 +
3103 + // A sentinel default distinguishes an absent option (leave auto-activation alone) from one
3104 + // explicitly stored falsey (an opt-out to preserve).
3105 + $stored = get_option( 'jetpack_ai_enabled', 'not-set' );
3106 + if ( 'not-set' !== $stored && ! (bool) $stored ) {
3107 + ( new Modules() )->deactivate( 'ai' );
3108 + }
3109 +
3110 + update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3111 + }
3112 +
3113 + /**
3114 + * Deletes obsolete SEO module-state options without changing module activation.
3115 + *
3116 + * @since 16.3
3117 + */
3118 + public static function cleanup_seo_module_state_options() {
3119 + delete_option( 'jetpack_seo_sitemap_enabled' );
3120 + delete_option( 'jetpack_seo_canonical_urls_enabled' );
3121 + delete_option( 'jetpack_seo_module_state_reconciled' );
3122 + }
3123 +
3124 + /**
3125 + * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3126 + * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3127 + *
3128 + * Hooked on `updating_jetpack_version`, which fires on every install including the
3129 + * first — with `$old_version === false` on a brand-new site (the same signal
3130 + * {@see self::activate_subscriptions_module_for_existing_sites()} keys off). Fresh
3131 + * installs are seeded visible; existing installs are seeded hidden and opt in later
3132 + * via the legacy Traffic page or My Jetpack. `add_option()` makes this seed-once: it
3133 + * never overrides a value a later opt-in (or opt-out) has set. WordPress.com sites
3134 + * ignore this option entirely (always visible) — see
3135 + * {@see \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()}.
3136 + *
3137 + * @param string $version The new Jetpack version (unused).
3138 + * @param string|false $old_version The previous version, or false on a fresh install.
3139 + */
3140 + public static function seed_seo_visibility_cohort( $version, $old_version ) {
3141 + add_option( Jetpack_SEO_Initializer::VISIBILITY_OPTION, ! $old_version );
3142 + }
3143 +
3144 + /**
2907 3145 * Sets the display_update_modal state.
3146 + *
3147 + * The release post update modal that read this state has been removed. The write is kept so the
3148 + * method still behaves as documented for the deprecation window. When this is deleted, also drop
3149 + * the matching `display_update_modal` guard in Automattic\Jetpack\CookieState::should_set_cookie(),
3150 + * which exists only to keep this key out of the cookie on the Jetpack admin screen.
3151 + *
3152 + * @deprecated 16.2
2908 3153 */
2909 3154 public static function set_update_modal_display() {
3155 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2910 3156 self::state( 'display_update_modal', true );
2911 3157 }
2912 3158
2913 3159 /**
@@ -2912,11 +3158,16 @@
2912 3158
2913 3159 /**
2914 3160 * Enqueues the block library styles.
2915 3161 *
3162 + * Only ever used by the release post update modal, which has been removed.
3163 + *
3164 + * @deprecated 16.2
3165 + *
2916 3166 * @param string $hook The current admin page.
2917 3167 */
2918 3168 public static function enqueue_block_style( $hook ) {
3169 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2919 3170 if ( 'toplevel_page_jetpack' === $hook ) {
2920 3171 wp_enqueue_style( 'wp-block-library' );
2921 3172 }
2922 3173 }
@@ -3005,8 +3256,12 @@
3005 3256 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
3006 3257 self::disconnect();
3007 3258 Jetpack_Options::delete_option( 'version' );
3008 3259 }
3260 +
3261 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
3262 + Woocommerce_Analytics::maybe_remove_proxy_speed_module();
3263 + }
3009 3264 }
3010 3265
3011 3266 /**
3012 3267 * Set activated option to 4 on jetpack_idc_disconnect action.
@@ -3034,9 +3289,9 @@
3034 3289 $connection->remove_connection( ! Identity_Crisis::validate_sync_error_idc_option() );
3035 3290 }
3036 3291
3037 3292 /**
3038 - * Happens after a successfull disconnection.
3293 + * Happens after a successful disconnection.
3039 3294 *
3040 3295 * @static
3041 3296 */
3042 3297 public static function jetpack_site_disconnected() {
@@ -3041,8 +3296,10 @@
3041 3296 */
3042 3297 public static function jetpack_site_disconnected() {
3043 3298 Identity_Crisis::clear_all_idc_options();
3044 3299
3300 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
3301 +
3045 3302 // Delete all the sync related data. Since it could be taking up space.
3046 3303 Sender::get_instance()->uninstall();
3047 3304
3048 3305 /**
@@ -3059,10 +3316,13 @@
3059 3316 }
3060 3317 }
3061 3318
3062 3319 /**
3063 - * Disconnects the user
3320 + * Disconnects the user.
3064 3321 *
3322 + * @deprecated 13.4
3323 + * @see \Automattic\Jetpack\Connection\Manager::disconnect_user()
3324 + *
3065 3325 * @param int $user_id The user ID to disconnect.
3066 3326 */
3067 3327 public function disconnect_user( $user_id ) {
3068 3328 $this->connection_manager->disconnect_user( $user_id );
@@ -3068,21 +3328,8 @@
3068 3328 $this->connection_manager->disconnect_user( $user_id );
3069 3329 }
3070 3330
3071 3331 /**
3072 - * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3073 - *
3074 - * @deprecated since Jetpack 9.7.0
3075 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
3076 - *
3077 - * @return bool|WP_Error
3078 - */
3079 - public static function try_registration() {
3080 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3081 - return static::connection()->try_registration();
3082 - }
3083 -
3084 - /**
3085 3332 * Checking the domain names in beta versions.
3086 3333 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3087 3334 *
3088 3335 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
@@ -3116,10 +3363,17 @@
3116 3363 * @param mixed $code Error code to log.
3117 3364 * @param mixed $data Data to log.
3118 3365 */
3119 3366 public static function log( $code, $data = null ) {
3367 +
3368 + $raw_log = Jetpack_Options::get_option( 'log', array() );
3369 + // This can be modified by the `jetpack_options` filter, so abort if we don't have an array.
3370 + if ( ! is_array( $raw_log ) ) {
3371 + return;
3372 + }
3373 +
3120 3374 // only grab the latest 200 entries.
3121 - $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3375 + $log = array_slice( $raw_log, -199, 199 );
3122 3376
3123 3377 // Append our event to the log.
3124 3378 $log_entry = array(
3125 3379 'time' => time(),
@@ -3219,8 +3473,15 @@
3219 3473
3220 3474 /**
3221 3475 * Return stat data for WPCOM sync.
3222 3476 *
3477 + * The Sync stats module was this method's last caller and moved to the Connection package's
3478 + * `Heartbeat::generate_stats_array()`, which assembles the heartbeat data through the
3479 + * `jetpack_heartbeat_stats_array` filter. Note the package method does not include the extended
3480 + * data from `get_additional_stat_data()`, so callers relying on `$extended` need to add it themselves.
3481 + *
3482 + * @deprecated 16.2
3483 + *
3223 3484 * @param bool $encode JSON encode the result.
3224 3485 * @param bool $extended Adds additional stats data.
3225 3486 *
3226 3487 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
@@ -3225,10 +3486,15 @@
3225 3486 *
3226 3487 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
3227 3488 */
3228 3489 public static function get_stat_data( $encode = true, $extended = true ) {
3229 - $data = Jetpack_Heartbeat::generate_stats_array();
3490 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3230 3491
3492 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
3493 + ? Heartbeat::get_environment_stats()
3494 + : array();
3495 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), $env_stats );
3496 +
3231 3497 if ( $extended ) {
3232 3498 $additional_data = self::get_additional_stat_data();
3233 3499 $data = array_merge( $data, $additional_data );
3234 3500 }
@@ -3233,9 +3499,9 @@
3233 3499 $data = array_merge( $data, $additional_data );
3234 3500 }
3235 3501
3236 3502 if ( $encode ) {
3237 - return wp_json_encode( $data );
3503 + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
3238 3504 }
3239 3505
3240 3506 return $data;
3241 3507 }
@@ -3314,12 +3580,17 @@
3314 3580 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3315 3581 // Upgrade: 1.1 -> 1.1.1
3316 3582 // Check and see if host can verify the Jetpack servers' SSL certificate.
3317 3583 $args = array();
3584 + // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
3318 3585 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3319 3586 }
3320 3587
3321 - if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3588 + if (
3589 + current_user_can( 'manage_options' )
3590 + && ! self::permit_ssl()
3591 + && ! $is_offline_mode
3592 + ) {
3322 3593 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3323 3594 }
3324 3595
3325 3596 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
@@ -3328,12 +3599,8 @@
3328 3599 if ( ! ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) && ! is_network_admin() ) ) {
3329 3600 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3330 3601 }
3331 3602
3332 - if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3333 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3334 - }
3335 -
3336 3603 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3337 3604
3338 3605 if ( self::is_connection_ready() || $is_offline_mode ) {
3339 3606 // Artificially throw errors in certain specific cases during plugin activation.
@@ -3372,8 +3639,9 @@
3372 3639 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3373 3640 * This function artificially throws errors for such cases (per a specific list).
3374 3641 *
3375 3642 * @param string $plugin The activated plugin.
3643 + * @throws RuntimeException If a conflicting plugin is detected.
3376 3644 */
3377 3645 public function throw_error_on_activate_plugin( $plugin ) {
3378 3646 $active_modules = self::get_active_modules();
3379 3647
@@ -3386,8 +3654,9 @@
3386 3654 if ( 'stats.php' === basename( $plugin ) ) {
3387 3655 $throw = true;
3388 3656 }
3389 3657 } else {
3658 + // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Checked above. See also https://github.com/phan/phan/issues/1204.
3390 3659 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3391 3660 if ( basename( $plugin ) === basename( $reflection->getFileName() ) ) {
3392 3661 $throw = true;
3393 3662 }
@@ -3394,9 +3663,9 @@
3394 3663 }
3395 3664
3396 3665 if ( $throw ) {
3397 3666 /* translators: Plugin name to deactivate. */
3398 - trigger_error( sprintf( esc_html__( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
3667 + throw new RuntimeException( sprintf( __( 'Jetpack contains the most recent version of the old "%1$s" plugin.', 'jetpack' ), 'WordPress.com Stats' ) );
3399 3668 }
3400 3669 }
3401 3670 }
3402 3671
@@ -3478,8 +3747,9 @@
3478 3747 /**
3479 3748 * Handler for Jetpack remote file uploads.
3480 3749 *
3481 3750 * @access public
3751 + * @return never
3482 3752 */
3483 3753 public function remote_request_handlers() {
3484 3754 switch ( current_filter() ) {
3485 3755 case 'wp_ajax_nopriv_jetpack_upload_file':
@@ -3506,18 +3776,17 @@
3506 3776 if ( ! is_int( $status_code ) ) {
3507 3777 $status_code = 400;
3508 3778 }
3509 3779
3510 - status_header( $status_code );
3511 - die( wp_json_encode( (object) compact( 'error', 'error_description' ) ) );
3780 + wp_send_json( (object) compact( 'error', 'error_description' ), $status_code, JSON_UNESCAPED_SLASHES );
3512 3781 }
3513 3782
3514 - status_header( 200 );
3515 3783 if ( true === $response ) {
3516 - exit;
3784 + status_header( 200 );
3785 + exit( 0 );
3517 3786 }
3518 3787
3519 - die( wp_json_encode( (object) $response ) );
3788 + wp_send_json( (object) $response, 200, JSON_UNESCAPED_SLASHES );
3520 3789 }
3521 3790
3522 3791 /**
3523 3792 * Uploads a file gotten from the global $_FILES.
@@ -3525,9 +3794,9 @@
3525 3794 * the attachment file of the media item (gotten through of the post_id)
3526 3795 * will be updated instead of add a new one.
3527 3796 *
3528 3797 * @param boolean $update_media_item - update media attachment.
3529 - * @return array - An array describing the uploadind files process.
3798 + * @return array|WP_Error - An array describing the uploading files process.
3530 3799 */
3531 3800 public function upload_handler( $update_media_item = false ) {
3532 3801 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
3533 3802 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
@@ -3578,9 +3847,9 @@
3578 3847 return new WP_Error( 'handler_cannot_upload', __( 'The upload handler cannot upload files', 'jetpack' ), 400 );
3579 3848 }
3580 3849
3581 3850 $uploaded_files = array();
3582 - $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3851 + $global_post = $GLOBALS['post'] ?? null;
3583 3852 unset( $GLOBALS['post'] );
3584 3853 if ( empty( $_FILES['media']['name'] ) ) {
3585 3854 // Nothing to process, just return.
3586 3855 return $uploaded_files;
@@ -3587,9 +3856,9 @@
3587 3856 }
3588 3857 foreach ( $_FILES['media']['name'] as $index => $name ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, unslash sniff is wrong. Validation should happen below.
3589 3858 $file = array();
3590 3859 foreach ( $media_keys as $media_key ) {
3591 - $file[ $media_key ] = isset( $_FILES['media'][ $media_key ][ $index ] ) ? $_FILES['media'][ $media_key ][ $index ] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3860 + $file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ] ?? null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,,WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3592 3861 }
3593 3862
3594 3863 list( $hmac_provided, $salt ) = isset( $_POST['_jetpack_file_hmac_media'][ $index ] ) ? explode( ':', filter_var( wp_unslash( $_POST['_jetpack_file_hmac_media'][ $index ] ) ) ) : array( 'no', '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce should have been checked by the caller.
3595 3864
@@ -3635,9 +3904,9 @@
3635 3904 'type' => (string) $edited_media_item->post_mime_type,
3636 3905 'meta' => (array) wp_get_attachment_metadata( $post_id ),
3637 3906 );
3638 3907
3639 - return (array) array( $response );
3908 + return array( $response );
3640 3909 }
3641 3910
3642 3911 $attachment_id = media_handle_upload(
3643 3912 '.jetpack.upload.',
@@ -3676,58 +3945,8 @@
3676 3945 return $uploaded_files;
3677 3946 }
3678 3947
3679 3948 /**
3680 - * Add help to the Jetpack page
3681 - *
3682 - * @since Jetpack (1.2.3)
3683 - * @return void
3684 - */
3685 - public function admin_help() {
3686 - $current_screen = get_current_screen();
3687 -
3688 - // Overview.
3689 - $current_screen->add_help_tab(
3690 - array(
3691 - 'id' => 'home',
3692 - 'title' => __( 'Home', 'jetpack' ),
3693 - 'content' =>
3694 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3695 - '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3696 - '<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>',
3697 - )
3698 - );
3699 -
3700 - // Screen Content.
3701 - if ( current_user_can( 'manage_options' ) ) {
3702 - $current_screen->add_help_tab(
3703 - array(
3704 - 'id' => 'settings',
3705 - 'title' => __( 'Settings', 'jetpack' ),
3706 - 'content' =>
3707 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3708 - '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3709 - '<ol>' .
3710 - '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3711 - '<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>' .
3712 - '</ol>' .
3713 - '<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>',
3714 - )
3715 - );
3716 - }
3717 -
3718 - // Help Sidebar.
3719 - $support_url = Redirect::get_url( 'jetpack-support' );
3720 - $faq_url = Redirect::get_url( 'jetpack-faq' );
3721 - $current_screen->set_help_sidebar(
3722 - '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3723 - '<p><a href="' . esc_url( $faq_url ) . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3724 - '<p><a href="' . esc_url( $support_url ) . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3725 - '<p><a href="' . esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3726 - );
3727 - }
3728 -
3729 - /**
3730 3949 * Add action links for the Jetpack plugin.
3731 3950 *
3732 3951 * @param array $actions Plugin actions.
3733 3952 *
@@ -3735,9 +3954,9 @@
3735 3954 */
3736 3955 public function plugin_action_links( $actions ) {
3737 3956 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
3738 3957 return array_merge(
3739 - array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3958 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack-settings#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3740 3959 $actions
3741 3960 );
3742 3961 }
3743 3962
@@ -3765,14 +3984,10 @@
3765 3984 'jetpack-plugins-page-js',
3766 3985 '_inc/build/plugins-page.js',
3767 3986 JETPACK__PLUGIN_FILE,
3768 3987 array(
3769 - 'in_footer' => true,
3770 - 'textdomain' => 'jetpack',
3771 - 'dependencies' => array(
3772 - 'wp-polyfill',
3773 - 'wp-components',
3774 - ),
3988 + 'in_footer' => true,
3989 + 'textdomain' => 'jetpack',
3775 3990 )
3776 3991 );
3777 3992 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3778 3993
@@ -3777,9 +3992,9 @@
3777 3992 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3778 3993
3779 3994 // Add objects to be passed to the initial state of the app.
3780 3995 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3781 - wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state() ) ) . '"));', 'before' );
3996 + wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_plugins_page_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
3782 3997
3783 3998 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
3784 3999 }
3785 4000 }
@@ -3828,9 +4043,9 @@
3828 4043 // @todo provide way to go to specific calypso env.
3829 4044 self::get_calypso_host() . 'jetpack/connect'
3830 4045 )
3831 4046 );
3832 - exit;
4047 + exit( 0 );
3833 4048 }
3834 4049 }
3835 4050
3836 4051 /*
@@ -3865,8 +4080,28 @@
3865 4080 * Done!
3866 4081 */
3867 4082
3868 4083 /**
4084 + * Build the user-facing description stored alongside a registration error code.
4085 + *
4086 + * @since 16.2
4087 + *
4088 + * @param string $error_code The WP_Error code.
4089 + * @param string $message The WP_Error message.
4090 + * @return string The description, empty when the message is not user-facing copy.
4091 + */
4092 + public static function get_registration_error_description( $error_code, $message ) {
4093 + // Manager::validate_remote_register_response() does not always put user-facing copy in the
4094 + // message slot: wpcom_5??, wpcom_408 and wpcom_bad_response store the HTTP status there,
4095 + // and jetpack_id stores the raw response body, which can also overflow the state cookie.
4096 + if ( 'jetpack_id' === $error_code || is_numeric( $message ) ) {
4097 + return '';
4098 + }
4099 +
4100 + return mb_substr( (string) $message, 0, 250 );
4101 + }
4102 +
4103 + /**
3869 4104 * Handles the page load events for the Jetpack admin page
3870 4105 */
3871 4106 public function admin_page_load() {
3872 4107 $error = false;
@@ -3902,10 +4137,11 @@
3902 4137 $registered = static::connection()->try_registration();
3903 4138 if ( is_wp_error( $registered ) ) {
3904 4139 $error = $registered->get_error_code();
3905 4140 self::state( 'error', $error );
3906 - self::state( 'error', $registered->get_error_message() );
3907 4141
4142 + self::state( 'error_description', self::get_registration_error_description( $error, $registered->get_error_message() ) );
4143 +
3908 4144 /**
3909 4145 * Jetpack registration Error.
3910 4146 *
3911 4147 * @since 7.5.0
@@ -3929,12 +4165,8 @@
3929 4165 do_action( 'jetpack_connection_register_success', $from );
3930 4166
3931 4167 $url = $this->build_connect_url( true, $redirect, $from );
3932 4168
3933 - if ( ! empty( $_GET['onboarding'] ) ) {
3934 - $url = add_query_arg( 'onboarding', rawurlencode_deep( wp_unslash( $_GET['onboarding'] ) ), $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3935 - }
3936 -
3937 4169 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
3938 4170 $url = add_query_arg( 'auth_approved', 'true', $url );
3939 4171 }
3940 4172
@@ -3939,9 +4171,9 @@
3939 4171 }
3940 4172
3941 4173 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3942 4174 wp_safe_redirect( $url );
3943 - exit;
4175 + exit( 0 );
3944 4176 case 'activate':
3945 4177 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3946 4178 $error = 'cheatin';
3947 4179 break;
@@ -3955,9 +4187,9 @@
3955 4187 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
3956 4188 }
3957 4189 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3958 4190 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3959 - exit;
4191 + exit( 0 );
3960 4192 case 'activate_default_modules':
3961 4193 check_admin_referer( 'activate_default_modules' );
3962 4194 self::log( 'activate_default_modules' );
3963 4195 self::restate();
@@ -3965,9 +4197,9 @@
3965 4197 $max_version = isset( $_GET['max_version'] ) ? sanitize_text_field( wp_unslash( $_GET['max_version'] ) ) : false;
3966 4198 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_GET['other_modules'] ) ) : array();
3967 4199 self::activate_default_modules( $min_version, $max_version, $other_modules );
3968 4200 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3969 - exit;
4201 + exit( 0 );
3970 4202 case 'disconnect':
3971 4203 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3972 4204 $error = 'cheatin';
3973 4205 break;
@@ -3976,9 +4208,9 @@
3976 4208 check_admin_referer( 'jetpack-disconnect' );
3977 4209 self::log( 'disconnect' );
3978 4210 self::disconnect();
3979 4211 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
3980 - exit;
4212 + exit( 0 );
3981 4213 case 'reconnect':
3982 4214 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
3983 4215 $error = 'cheatin';
3984 4216 break;
@@ -3989,9 +4221,9 @@
3989 4221 self::disconnect();
3990 4222
3991 4223 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3992 4224 wp_safe_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
3993 - exit;
4225 + exit( 0 );
3994 4226 case 'deactivate':
3995 4227 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
3996 4228 $error = 'cheatin';
3997 4229 break;
@@ -4005,9 +4237,9 @@
4005 4237 self::state( 'message', 'module_deactivated' );
4006 4238 }
4007 4239 self::state( 'module', $modules );
4008 4240 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4009 - exit;
4241 + exit( 0 );
4010 4242 case 'unlink':
4011 4243 $redirect = isset( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : '';
4012 4244 check_admin_referer( 'jetpack-unlink' );
4013 4245 self::log( 'unlink' );
@@ -4017,32 +4249,9 @@
4017 4249 wp_safe_redirect( admin_url() );
4018 4250 } else {
4019 4251 wp_safe_redirect( self::admin_url( array( 'page' => rawurlencode( $redirect ) ) ) );
4020 4252 }
4021 - exit;
4022 - case 'onboard':
4023 - if ( ! current_user_can( 'manage_options' ) ) {
4024 - wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4025 - } else {
4026 - self::create_onboarding_token();
4027 - $url = $this->build_connect_url( true );
4028 -
4029 - $token = Jetpack_Options::get_option( 'onboarding' );
4030 -
4031 - if ( false !== ( $token ) ) {
4032 - $url = add_query_arg( 'onboarding', $token, $url );
4033 - }
4034 -
4035 - $calypso_env = ( new Host() )->get_calypso_env();
4036 - if ( ! empty( $calypso_env ) ) {
4037 - $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4038 - }
4039 -
4040 - add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4041 - wp_safe_redirect( $url );
4042 - exit;
4043 - }
4044 - exit;
4253 + exit( 0 );
4045 4254 default:
4046 4255 /**
4047 4256 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4048 4257 *
@@ -4265,37 +4474,8 @@
4265 4474 endif;
4266 4475 }
4267 4476
4268 4477 /**
4269 - * We can't always respond to a signed XML-RPC request with a
4270 - * helpful error message. In some circumstances, doing so could
4271 - * leak information.
4272 - *
4273 - * Instead, track that the error occurred via a Jetpack_Option,
4274 - * and send that data back in the heartbeat.
4275 - * All this does is increment a number, but it's enough to find
4276 - * trends.
4277 - *
4278 - * @param WP_Error $xmlrpc_error The error produced during
4279 - * signature validation.
4280 - */
4281 - public function track_xmlrpc_error( $xmlrpc_error ) {
4282 - $code = is_wp_error( $xmlrpc_error )
4283 - ? $xmlrpc_error->get_error_code()
4284 - : 'should-not-happen';
4285 -
4286 - $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4287 - if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4288 - // No need to update the option if we already have
4289 - // this code stored.
4290 - return;
4291 - }
4292 - $xmlrpc_errors[ $code ] = true;
4293 -
4294 - Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4295 - }
4296 -
4297 - /**
4298 4478 * Initialize the jetpack stats instance only when needed
4299 4479 *
4300 4480 * @return void
4301 4481 */
@@ -4418,9 +4598,9 @@
4418 4598 return $this->build_connect_url( $raw, $redirect, $from, true );
4419 4599 }
4420 4600 }
4421 4601
4422 - $url = static::build_authorize_url( $redirect );
4602 + $url = ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4423 4603 }
4424 4604
4425 4605 if ( $from ) {
4426 4606 $url = add_query_arg( 'from', $from, $url );
@@ -4445,66 +4625,30 @@
4445 4625 * @param null $deprecated Deprecated since Jetpack 10.9.
4446 4626 *
4447 4627 * @todo Update default value for redirect since the called function expects a string.
4448 4628 *
4629 + * @deprecated 13.4
4630 + *
4449 4631 * @return mixed|void
4450 4632 */
4451 4633 public static function build_authorize_url( $redirect = false, $deprecated = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
4634 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::build_authorize_url' );
4452 4635
4453 - add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4454 - add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4455 -
4456 - $c8n = self::connection();
4457 - $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4458 -
4459 - remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4460 - remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4461 -
4462 - /**
4463 - * Filter the URL used when authorizing a user to a WordPress.com account.
4464 - *
4465 - * @since 8.9.0
4466 - *
4467 - * @param string $url Connection URL.
4468 - */
4469 - return apply_filters( 'jetpack_build_authorize_url', $url );
4636 + return ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4470 4637 }
4471 4638
4472 4639 /**
4473 4640 * Filters the connection URL parameter array.
4474 4641 *
4642 + * @deprecated 13.4
4643 + *
4475 4644 * @param array $args default URL parameters used by the package.
4476 4645 * @return array the modified URL arguments array.
4477 4646 */
4478 4647 public static function filter_connect_request_body( $args ) {
4479 - if (
4480 - Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4481 - && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4482 - ) {
4483 - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4484 - $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4485 - ? $gp_locale->slug
4486 - : '';
4487 - }
4648 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_request_body' );
4488 4649
4489 - $tracking = new Tracking();
4490 - $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4491 -
4492 - $args = array_merge(
4493 - $args,
4494 - array(
4495 - '_ui' => $tracks_identity['_ui'],
4496 - '_ut' => $tracks_identity['_ut'],
4497 - )
4498 - );
4499 -
4500 - $calypso_env = ( new Host() )->get_calypso_env();
4501 -
4502 - if ( ! empty( $calypso_env ) ) {
4503 - $args['calypso_env'] = $calypso_env;
4504 - }
4505 -
4506 - return $args;
4650 + return Authorize_Redirect::filter_connect_request_body( $args );
4507 4651 }
4508 4652
4509 4653 /**
4510 4654 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
@@ -4541,41 +4685,19 @@
4541 4685 return $current_user_connection_data;
4542 4686 }
4543 4687
4544 4688 /**
4545 - * Filters the URL that will process the connection data. It can be different from the URL
4546 - * that we send the user to after everything is done.
4547 - *
4548 - * @param String $processing_url the default redirect URL used by the package.
4549 - * @return String the modified URL.
4550 - *
4551 - * @deprecated since Jetpack 9.5.0
4552 - */
4553 - public static function filter_connect_processing_url( $processing_url ) {
4554 - _deprecated_function( __METHOD__, 'jetpack-9.5' );
4555 -
4556 - $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4557 - return $processing_url;
4558 - }
4559 -
4560 - /**
4561 4689 * Filters the redirection URL that is used for connect requests. The redirect
4562 4690 * URL should return the user back to the Jetpack console.
4563 4691 *
4692 + * @deprecated 13.4
4693 + *
4564 4694 * @param String $redirect the default redirect URL used by the package.
4565 4695 * @return String the modified URL.
4566 4696 */
4567 4697 public static function filter_connect_redirect_url( $redirect ) {
4568 - $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4569 - $redirect = $redirect
4570 - ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4571 - : $jetpack_admin_page;
4572 -
4573 - if ( isset( $_REQUEST['is_multisite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- not making a site change here.
4574 - $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4575 - }
4576 -
4577 - return $redirect;
4698 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_redirect_url' );
4699 + return Authorize_Redirect::filter_connect_redirect_url( $redirect );
4578 4700 }
4579 4701
4580 4702 /**
4581 4703 * This action fires at the beginning of the Manager::authorize method.
@@ -4639,11 +4761,8 @@
4639 4761 *
4640 4762 * @param array $data The request data.
4641 4763 */
4642 4764 public static function authorize_ending_authorized( $data ) {
4643 - // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4644 - self::invalidate_onboarding_token();
4645 -
4646 4765 // If redirect_uri is SSO, ensure SSO module is enabled.
4647 4766 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4648 4767
4649 4768 /** This filter is documented in class.jetpack-cli.php */
@@ -4783,10 +4902,12 @@
4783 4902 $result = self::permit_ssl( true );
4784 4903 wp_send_json(
4785 4904 array(
4786 4905 'enabled' => $result,
4787 - 'message' => get_transient( 'jetpack_https_test_message' ),
4788 - )
4906 + 'message' => self::get_ssl_test_message(),
4907 + ),
4908 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
4909 + JSON_UNESCAPED_SLASHES
4789 4910 );
4790 4911 }
4791 4912
4792 4913 /* Client API */
@@ -4793,8 +4914,10 @@
4793 4914
4794 4915 /**
4795 4916 * Verify the onboarding token.
4796 4917 *
4918 + * @deprecated since 13.9
4919 + *
4797 4920 * @param array $token_data Token data.
4798 4921 * @param string $token Token value.
4799 4922 * @param string $request_data JSON-encoded request data.
4800 4923 *
@@ -4800,8 +4923,9 @@
4800 4923 *
4801 4924 * @return mixed
4802 4925 */
4803 4926 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
4927 + _deprecated_function( __METHOD__, '13.9' );
4804 4928 // Default to a blog token.
4805 4929 $token_type = 'blog';
4806 4930
4807 4931 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
@@ -4829,9 +4953,9 @@
4829 4953 $jp_user = get_user_by( 'email', $jpo_user );
4830 4954 if ( is_a( $jp_user, 'WP_User' ) ) {
4831 4955 wp_set_current_user( $jp_user->ID );
4832 4956 $user_can = is_multisite()
4833 - ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
4957 + ? current_user_can_for_site( get_current_blog_id(), 'manage_options' )
4834 4958 : current_user_can( 'manage_options' );
4835 4959 if ( $user_can ) {
4836 4960 $token_type = 'user';
4837 4961 $token->external_user_id = $jp_user->ID;
@@ -4848,11 +4972,13 @@
4848 4972
4849 4973 /**
4850 4974 * Create a random secret for validating onboarding payload
4851 4975 *
4976 + * @deprecated since 13.9
4852 4977 * @return string Secret token
4853 4978 */
4854 4979 public static function create_onboarding_token() {
4980 + _deprecated_function( __METHOD__, '13.9' );
4855 4981 $token = Jetpack_Options::get_option( 'onboarding' );
4856 4982 if ( false === ( $token ) ) {
4857 4983 $token = wp_generate_password( 32, false );
4858 4984 Jetpack_Options::update_option( 'onboarding', $token );
@@ -4863,11 +4989,13 @@
4863 4989
4864 4990 /**
4865 4991 * Remove the onboarding token
4866 4992 *
4993 + * @deprecated since 13.9
4867 4994 * @return bool True on success, false on failure
4868 4995 */
4869 4996 public static function invalidate_onboarding_token() {
4997 + _deprecated_function( __METHOD__, '13.9' );
4870 4998 return Jetpack_Options::delete_option( 'onboarding' );
4871 4999 }
4872 5000
4873 5001 /**
@@ -4872,8 +5000,10 @@
4872 5000
4873 5001 /**
4874 5002 * Validate an onboarding token for a specific action
4875 5003 *
5004 + * @deprecated since 13.9
5005 + *
4876 5006 * @param string $token Onboarding token.
4877 5007 * @param string $action Action name.
4878 5008 *
4879 5009 * @return boolean True if token/action pair is accepted, false if not
@@ -4878,8 +5008,9 @@
4878 5008 *
4879 5009 * @return boolean True if token/action pair is accepted, false if not
4880 5010 */
4881 5011 public static function validate_onboarding_token_action( $token, $action ) {
5012 + _deprecated_function( __METHOD__, '13.9' );
4882 5013 // Compare tokens, bail if tokens do not match.
4883 5014 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4884 5015 return false;
4885 5016 }
@@ -4905,39 +5036,41 @@
4905 5036 * @return boolean
4906 5037 * @since 2.3.3
4907 5038 */
4908 5039 public static function permit_ssl( $force_recheck = false ) {
4909 - // Do some fancy tests to see if ssl is being supported.
4910 - if ( ! $force_recheck ) {
4911 - $ssl = get_transient( 'jetpack_https_test' );
5040 + if ( ! method_exists( Heartbeat::class, 'permit_ssl' ) ) {
5041 + // Skip the SSL-fail notice when the check cannot run.
5042 + return true;
4912 5043 }
4913 5044
4914 - if ( $force_recheck || false === $ssl ) {
4915 - $message = '';
4916 - if ( ! str_starts_with( JETPACK__API_BASE, 'https' ) ) {
4917 - $ssl = 0;
4918 - } else {
4919 - $ssl = 1;
5045 + return Heartbeat::permit_ssl( $force_recheck );
5046 + }
4920 5047
4921 - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4922 - $ssl = 0;
4923 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4924 - } else {
4925 - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4926 - if ( is_wp_error( $response ) ) {
4927 - $ssl = 0;
4928 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4929 - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4930 - $ssl = 0;
4931 - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4932 - }
4933 - }
4934 - }
4935 - set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4936 - set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5048 + /**
5049 + * Returns a localized message describing the last SSL connectivity failure, if any.
5050 + *
5051 + * The Connection package's canonical SSL check stores a neutral reason code; this maps it to
5052 + * a translated, `jetpack`-domain message for display in the admin notice and AJAX recheck.
5053 + *
5054 + * @since 16.1
5055 + *
5056 + * @return string The localized message, or an empty string when there is no failure.
5057 + */
5058 + public static function get_ssl_test_message() {
5059 + if ( ! method_exists( Heartbeat::class, 'get_ssl_test_error' ) ) {
5060 + return '';
4937 5061 }
4938 5062
4939 - return (bool) $ssl;
5063 + $error = Heartbeat::get_ssl_test_error();
5064 +
5065 + switch ( $error['code'] ) {
5066 + case 'no_ssl_support':
5067 + return __( 'WordPress reports no SSL support', 'jetpack' );
5068 + case 'bad_response':
5069 + return __( 'Response was not OK: ', 'jetpack' ) . $error['detail'];
5070 + default:
5071 + return '';
5072 + }
4940 5073 }
4941 5074
4942 5075 /**
4943 5076 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
@@ -4956,9 +5089,9 @@
4956 5089 <p><?php esc_html_e( 'Your site could not connect to WordPress.com via HTTPS. This could be due to any number of reasons, including faulty SSL certificates, misconfigured or missing SSL libraries, or network issues.', 'jetpack' ); ?></p>
4957 5090 <p>
4958 5091 <?php esc_html_e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
4959 5092 <a href="#" id="jetpack-recheck-ssl-button"><?php esc_html_e( 'Try again', 'jetpack' ); ?></a>
4960 - <span id="jetpack-recheck-ssl-output"><?php echo esc_html( get_transient( 'jetpack_https_test_message' ) ); ?></span>
5093 + <span id="jetpack-recheck-ssl-output"><?php echo esc_html( self::get_ssl_test_message() ); ?></span>
4961 5094 </p>
4962 5095 <p>
4963 5096 <?php
4964 5097 printf(
@@ -4977,18 +5110,18 @@
4977 5110 <script type="text/javascript">
4978 5111 jQuery( document ).ready( function( $ ) {
4979 5112 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
4980 5113 var $this = $( this );
4981 - $this.html( <?php echo wp_json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5114 + $this.html( <?php echo wp_json_encode( esc_html__( 'Checking', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
4982 5115 $( '#jetpack-recheck-ssl-output' ).html( '' );
4983 5116 e.preventDefault();
4984 - var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce ); ?> };
5117 + var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> };
4985 5118 $.post( ajaxurl, data )
4986 5119 .done( function( response ) {
4987 5120 if ( response.enabled ) {
4988 5121 $( '#jetpack-ssl-warning' ).hide();
4989 5122 } else {
4990 - this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5123 + this.html( <?php echo wp_json_encode( esc_html__( 'Try again', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
4991 5124 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
4992 5125 }
4993 5126 }.bind( $this ) );
4994 5127 } );
@@ -5014,26 +5147,8 @@
5014 5147 return $jetpack->connection_manager;
5015 5148 }
5016 5149
5017 5150 /**
5018 - * Creates two secret tokens and the end of life timestamp for them.
5019 - *
5020 - * Note these tokens are unique per call, NOT static per site for connecting.
5021 - *
5022 - * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5023 - *
5024 - * @since 2.6
5025 - * @param String $action The action name.
5026 - * @param Integer $user_id The user identifier.
5027 - * @param Integer $exp Expiration time in seconds.
5028 - * @return array
5029 - */
5030 - public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5031 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5032 - return self::connection()->generate_secrets( $action, $user_id, $exp );
5033 - }
5034 -
5035 - /**
5036 5151 * Get verification secrets.
5037 5152 *
5038 5153 * @param string $action Action name.
5039 5154 * @param int $user_id User ID.
@@ -5054,35 +5169,8 @@
5054 5169 return $secrets;
5055 5170 }
5056 5171
5057 5172 /**
5058 - * Register a connection.
5059 - *
5060 - * @deprecated Jetpack 9.7.0
5061 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
5062 - *
5063 - * @return bool|WP_Error
5064 - */
5065 - public static function register() {
5066 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5067 - return static::connection()->try_registration( false );
5068 - }
5069 -
5070 - /**
5071 - * Filters the registration request body to include tracking properties.
5072 - *
5073 - * @deprecated Jetpack 9.7.0
5074 - * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5075 - *
5076 - * @param array $properties Token request properties.
5077 - * @return array amended properties.
5078 - */
5079 - public static function filter_register_request_body( $properties ) {
5080 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5081 - return Connection_Utils::filter_register_request_body( $properties );
5082 - }
5083 -
5084 - /**
5085 5173 * Filters the token request body to include tracking properties.
5086 5174 *
5087 5175 * @param array $properties Token request properties.
5088 5176 *
@@ -5103,9 +5191,9 @@
5103 5191
5104 5192 /**
5105 5193 * If the db version is showing something other that what we've got now, bump it to current.
5106 5194 *
5107 - * @return bool: True if the option was incorrect and updated, false if nothing happened.
5195 + * @return bool True if the option was incorrect and updated, false if nothing happened.
5108 5196 */
5109 5197 public static function maybe_set_version_option() {
5110 5198 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5111 5199 if ( JETPACK__VERSION !== $version ) {
@@ -5123,21 +5211,8 @@
5123 5211
5124 5212 /* Client Server API */
5125 5213
5126 5214 /**
5127 - * Loads the Jetpack XML-RPC client.
5128 - * No longer necessary, as the XML-RPC client will be automagically loaded.
5129 - *
5130 - * Note: we cannot remove this function yet as it is used in this plugin:
5131 - * https://wordpress.org/plugins/jetpack-subscription-form/
5132 - *
5133 - * @deprecated since 7.7.0
5134 - */
5135 - public static function load_xml_rpc_client() {
5136 - _deprecated_function( __METHOD__, 'jetpack-7.7' );
5137 - }
5138 -
5139 - /**
5140 5215 * State is passed via cookies from one request to the next, but never to subsequent requests.
5141 5216 * SET: state( $key, $value );
5142 5217 * GET: $value = state( $key );
5143 5218 *
@@ -5211,20 +5286,8 @@
5211 5286
5212 5287 self::state( 'privacy_checks', $privacy_checks );
5213 5288 }
5214 5289
5215 - /**
5216 - * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5217 - *
5218 - * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5219 - *
5220 - * @param string $url WordPress.com static resource URL.
5221 - */
5222 - public static function staticize_subdomain( $url ) {
5223 - _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5224 - return Assets::staticize_subdomain( $url );
5225 - }
5226 -
5227 5290 /* JSON API Authorization */
5228 5291
5229 5292 /**
5230 5293 * Handles the login action for Authorizing the JSON API
@@ -5229,13 +5292,14 @@
5229 5292 /**
5230 5293 * Handles the login action for Authorizing the JSON API
5231 5294 */
5232 5295 public function login_form_json_api_authorization() {
5233 - $this->verify_json_api_authorization_request();
5296 + $authorize_json_api = new Authorize_Json_Api();
5297 + $authorize_json_api->verify_json_api_authorization_request();
5234 5298
5235 - add_action( 'wp_login', array( $this, 'store_json_api_authorization_token' ), 10, 2 );
5299 + add_action( 'wp_login', array( $authorize_json_api, 'store_json_api_authorization_token' ), 10, 2 );
5236 5300
5237 - add_action( 'login_message', array( $this, 'login_message_json_api_authorization' ) );
5301 + add_action( 'login_message', array( $authorize_json_api, 'login_message_json_api_authorization' ) );
5238 5302 add_action( 'login_form', array( $this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5239 5303 add_filter( 'site_url', array( $this, 'post_login_form_to_signed_url' ), 10, 3 );
5240 5304 }
5241 5305
@@ -5273,16 +5337,17 @@
5273 5337
5274 5338 /**
5275 5339 * If someone logs in to approve API access, store the Access Code in usermeta.
5276 5340 *
5341 + * @deprecated 13.4
5342 + *
5277 5343 * @param string $user_login Unused.
5278 5344 * @param WP_User $user User logged in.
5279 5345 */
5280 5346 public function store_json_api_authorization_token( $user_login, $user ) {
5281 - add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5282 - add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_public_api_domain' ) );
5283 - $token = wp_generate_password( 32, false );
5284 - update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5347 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::store_json_api_authorization_token' );
5348 +
5349 + return ( new Authorize_Json_Api() )->store_json_api_authorization_token( $user_login, $user );
5285 5350 }
5286 5351
5287 5352 /**
5288 5353 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
@@ -5288,23 +5353,29 @@
5288 5353 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5289 5354 *
5290 5355 * To be used with a filter of allowed domains for a redirect.
5291 5356 *
5357 + * @deprecated 13.4
5358 + *
5292 5359 * @param array $domains Allowed WP.com Environments.
5293 5360 */
5294 5361 public function allow_wpcom_public_api_domain( $domains ) {
5295 - $domains[] = 'public-api.wordpress.com';
5296 - return $domains;
5362 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Status\\Host::allow_wpcom_public_api_domain' );
5363 +
5364 + return Host::allow_wpcom_public_api_domain( $domains );
5297 5365 }
5298 5366
5299 5367 /**
5300 5368 * Check if the redirect is encoded.
5301 5369 *
5370 + * @deprecated 13.4
5371 + *
5302 5372 * @param string $redirect_url Redirect URL.
5303 5373 *
5304 5374 * @return bool If redirect has been encoded.
5305 5375 */
5306 5376 public static function is_redirect_encoded( $redirect_url ) {
5377 + _deprecated_function( __METHOD__, 'jetpack-13.4' );
5307 5378 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5308 5379 }
5309 5380
5310 5381 /**
@@ -5322,8 +5393,10 @@
5322 5393
5323 5394 /**
5324 5395 * Add the Access Code details to the public-api.wordpress.com redirect.
5325 5396 *
5397 + * @deprecated 13.4
5398 + *
5326 5399 * @param string $redirect_to URL.
5327 5400 * @param string $original_redirect_to URL.
5328 5401 * @param WP_User $user WP_User for the redirect.
5329 5402 *
@@ -5329,23 +5402,18 @@
5329 5402 *
5330 5403 * @return string
5331 5404 */
5332 5405 public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5333 - return add_query_arg(
5334 - urlencode_deep(
5335 - array(
5336 - 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5337 - 'jetpack-user-id' => (int) $user->ID,
5338 - 'jetpack-state' => $this->json_api_authorization_request['state'],
5339 - )
5340 - ),
5341 - $redirect_to
5342 - );
5406 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::add_token_to_login_redirect_json_api_authorization' );
5407 +
5408 + return ( new Authorize_Json_Api() )->add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user );
5343 5409 }
5344 5410
5345 5411 /**
5346 5412 * Verifies the request by checking the signature
5347 5413 *
5414 + * @deprecated 13.4
5415 + *
5348 5416 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5349 5417 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5350 5418 *
5351 5419 * @param null|array $environment Value to override $_REQUEST.
@@ -5350,194 +5418,24 @@
5350 5418 *
5351 5419 * @param null|array $environment Value to override $_REQUEST.
5352 5420 */
5353 5421 public function verify_json_api_authorization_request( $environment = null ) {
5354 - $environment = $environment === null
5355 - ? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function.
5356 - : $environment;
5422 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::verify_json_api_authorization_request' );
5357 5423
5358 - list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
5359 - $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
5360 - if ( ! $token || empty( $token->secret ) ) {
5361 - wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5362 - }
5363 -
5364 - $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' );
5365 -
5366 - // Host has encoded the request URL, probably as a result of a bad http => https redirect.
5367 - 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.
5368 - /**
5369 - * Jetpack authorisation request Error.
5370 - *
5371 - * @since 7.5.0
5372 - */
5373 - do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5374 - $die_error = sprintf(
5375 - /* translators: %s is a URL */
5376 - __( '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' ),
5377 - esc_url( Redirect::get_url( 'jetpack-support-double-encoding' ) )
5378 - );
5379 - }
5380 -
5381 - $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5382 -
5383 - if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5384 - $signature = $jetpack_signature->sign_request(
5385 - $environment['token'],
5386 - $environment['timestamp'],
5387 - $environment['nonce'],
5388 - '',
5389 - 'GET',
5390 - $environment['jetpack_json_api_original_query'],
5391 - null,
5392 - true
5393 - );
5394 - } else {
5395 - $signature = $jetpack_signature->sign_current_request(
5396 - array(
5397 - 'body' => null,
5398 - 'method' => 'GET',
5399 - )
5400 - );
5401 - }
5402 -
5403 - if ( ! $signature ) {
5404 - wp_die(
5405 - wp_kses(
5406 - $die_error,
5407 - array(
5408 - 'a' => array(
5409 - 'href' => array(),
5410 - ),
5411 - )
5412 - )
5413 - );
5414 - } elseif ( is_wp_error( $signature ) ) {
5415 - wp_die(
5416 - wp_kses(
5417 - $die_error,
5418 - array(
5419 - 'a' => array(
5420 - 'href' => array(),
5421 - ),
5422 - )
5423 - )
5424 - );
5425 - } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5426 - if ( is_ssl() ) {
5427 - // 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.
5428 - $signature = $jetpack_signature->sign_current_request(
5429 - array(
5430 - 'scheme' => 'http',
5431 - 'body' => null,
5432 - 'method' => 'GET',
5433 - )
5434 - );
5435 - if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5436 - wp_die(
5437 - wp_kses(
5438 - $die_error,
5439 - array(
5440 - 'a' => array(
5441 - 'href' => array(),
5442 - ),
5443 - )
5444 - )
5445 - );
5446 - }
5447 - } else {
5448 - wp_die(
5449 - wp_kses(
5450 - $die_error,
5451 - array(
5452 - 'a' => array(
5453 - 'href' => array(),
5454 - ),
5455 - )
5456 - )
5457 - );
5458 - }
5459 - }
5460 -
5461 - $timestamp = (int) $environment['timestamp'];
5462 - $nonce = stripslashes( (string) $environment['nonce'] );
5463 -
5464 - if ( ! $this->connection_manager ) {
5465 - $this->connection_manager = new Connection_Manager();
5466 - }
5467 -
5468 - if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
5469 - // De-nonce the nonce, at least for 5 minutes.
5470 - // 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).
5471 - $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5472 - if ( $old_nonce_time < time() - 300 ) {
5473 - wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
5474 - }
5475 - }
5476 -
5477 - $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
5478 - $data_filters = array(
5479 - 'state' => 'opaque',
5480 - 'client_id' => 'int',
5481 - 'client_title' => 'string',
5482 - 'client_image' => 'url',
5483 - );
5484 -
5485 - foreach ( $data_filters as $key => $sanitation ) {
5486 - if ( ! isset( $data->$key ) ) {
5487 - wp_die(
5488 - wp_kses(
5489 - $die_error,
5490 - array(
5491 - 'a' => array(
5492 - 'href' => array(),
5493 - ),
5494 - )
5495 - )
5496 - );
5497 - }
5498 -
5499 - switch ( $sanitation ) {
5500 - case 'int':
5501 - $this->json_api_authorization_request[ $key ] = (int) $data->$key;
5502 - break;
5503 - case 'opaque':
5504 - $this->json_api_authorization_request[ $key ] = (string) $data->$key;
5505 - break;
5506 - case 'string':
5507 - $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
5508 - break;
5509 - case 'url':
5510 - $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
5511 - break;
5512 - }
5513 - }
5514 -
5515 - if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5516 - wp_die(
5517 - wp_kses(
5518 - $die_error,
5519 - array(
5520 - 'a' => array(
5521 - 'href' => array(),
5522 - ),
5523 - )
5524 - )
5525 - );
5526 - }
5424 + return ( new Authorize_Json_Api() )->verify_json_api_authorization_request( $environment );
5527 5425 }
5528 5426
5529 5427 /**
5530 5428 * HTML for the JSON API authorization notice.
5531 5429 *
5430 + * @deprecated 13.4
5431 + *
5532 5432 * @return string
5533 5433 */
5534 5434 public function login_message_json_api_authorization() {
5535 - return '<p class="message">' . sprintf(
5536 - /* translators: Name/image of the client requesting authorization */
5537 - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
5538 - '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5539 - ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5435 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::login_message_json_api_authorization' );
5436 +
5437 + return ( new Authorize_Json_Api() )->login_message_json_api_authorization();
5540 5438 }
5541 5439
5542 5440 /**
5543 5441 * Get $content_width, but with a <s>twist</s> filter.
@@ -5581,15 +5479,20 @@
5581 5479
5582 5480 /**
5583 5481 * Checks if the site is currently in an identity crisis.
5584 5482 *
5483 + * Now delegates to the Connection package so this matches what the heartbeat itself reports.
5484 + * Note the package guards on `Connection\Manager::is_connected()` where this used to guard on
5485 + * `Jetpack::is_connection_ready()`, so the `jetpack_is_connection_ready` filter no longer applies.
5486 + *
5487 + * @deprecated 16.2
5488 + *
5585 5489 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
5586 5490 */
5587 5491 public static function check_identity_crisis() {
5588 - if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
5589 - return false;
5590 - }
5591 - return Jetpack_Options::get_option( 'sync_error_idc' );
5492 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Identity_Crisis::check_identity_crisis' );
5493 +
5494 + return Identity_Crisis::check_identity_crisis();
5592 5495 }
5593 5496
5594 5497 /**
5595 5498 * Normalizes a url by doing three things:
@@ -5677,9 +5580,9 @@
5677 5580 *
5678 5581 * @return mixed
5679 5582 */
5680 5583 public static function set_suffix_on_min( $src, $handle ) {
5681 - if ( ! str_contains( $src, '.min.css' ) ) {
5584 + if ( ! is_string( $src ) || ! str_contains( $src, '.min.css' ) ) {
5682 5585 return $src;
5683 5586 }
5684 5587
5685 5588 if ( ! empty( self::$min_assets ) ) {
@@ -5715,8 +5618,10 @@
5715 5618 *
5716 5619 * Data passed in with the $data parameter will be available in the
5717 5620 * template file as $data['value']
5718 5621 *
5622 + * @html-template-var array $data
5623 + *
5719 5624 * @param string $template - Template file to load.
5720 5625 * @param array $data - Any data to pass along to the template.
5721 5626 * @return boolean - If template file was found.
5722 5627 **/
@@ -5734,8 +5639,19 @@
5734 5639 return false;
5735 5640 }
5736 5641
5737 5642 /**
5643 + * Register Jetpack-specific tests on the connection package's health test suite.
5644 + *
5645 + * @param \Automattic\Jetpack\Connection\Connection_Health_Tests $connection_tests The test suite instance.
5646 + */
5647 + public function register_jetpack_connection_tests( $connection_tests ) {
5648 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger/class-jetpack-cxn-tests.php';
5649 + $jetpack_tests = new Jetpack_Cxn_Tests();
5650 + $jetpack_tests->register_tests_on( $connection_tests );
5651 + }
5652 +
5653 + /**
5738 5654 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
5739 5655 */
5740 5656 public function deprecated_hooks() {
5741 5657 $filter_deprecated_list = array(
@@ -5955,8 +5871,14 @@
5955 5871 'jetpack_pre_connection_prompt_helpers' => array(
5956 5872 'replacement' => null,
5957 5873 'version' => 'jetpack-13.2.0',
5958 5874 ),
5875 + 'jetpack_contact_form_use_package' => array(
5876 + 'replacement' => null,
5877 + 'version' => 'jetpack-13.4.0',
5878 + ),
5879 + // jetpack_implode_frontend_css has been removed, but is not listed here. The updated behavior is exactly the only use of the filter.
5880 + // We can reassess formally deprecating it here later; for now, it would be noise with no functional difference.
5959 5881 );
5960 5882
5961 5883 foreach ( $filter_deprecated_list as $tag => $args ) {
5962 5884 if ( has_filter( $tag ) ) {
@@ -6087,125 +6009,8 @@
6087 6009 return $css;
6088 6010 }
6089 6011
6090 6012 /**
6091 - * This methods removes all of the registered css files on the front end
6092 - * from Jetpack in favor of using a single file. In effect "imploding"
6093 - * all the files into one file.
6094 - *
6095 - * Pros:
6096 - * - Uses only ONE css asset connection instead of 15
6097 - * - Saves a minimum of 56k
6098 - * - Reduces server load
6099 - * - Reduces time to first painted byte
6100 - *
6101 - * Cons:
6102 - * - Loads css for ALL modules. However all selectors are prefixed so it
6103 - * should not cause any issues with themes.
6104 - * - Plugins/themes dequeuing styles no longer do anything. See
6105 - * jetpack_implode_frontend_css filter for a workaround
6106 - *
6107 - * For some situations developers may wish to disable css imploding and
6108 - * instead operate in legacy mode where each file loads seperately and
6109 - * can be edited individually or dequeued. This can be accomplished with
6110 - * the following line:
6111 - *
6112 - * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6113 - *
6114 - * @param bool $travis_test Is this a test run.
6115 - *
6116 - * @since 3.2
6117 - */
6118 - public function implode_frontend_css( $travis_test = false ) {
6119 - $do_implode = true;
6120 - if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6121 - $do_implode = false;
6122 - }
6123 -
6124 - // Do not implode CSS when the page loads via the AMP plugin.
6125 - if ( class_exists( Jetpack_AMP_Support::class ) && Jetpack_AMP_Support::is_amp_request() ) {
6126 - $do_implode = false;
6127 - }
6128 -
6129 - /*
6130 - * Only proceed if at least 2 modules with concatenated CSS are active.
6131 - * There is no point in serving a big concatenated CSS file
6132 - * if there are no features (or only one) that actually need some CSS loaded.
6133 - */
6134 - $active_modules = self::get_active_modules();
6135 - $modules_with_concatenated_css = $this->modules_with_concatenated_css;
6136 - $active_module_with_css_count = count( array_intersect( $active_modules, $modules_with_concatenated_css ) );
6137 - if ( $active_module_with_css_count < 2 ) {
6138 - $do_implode = false;
6139 - }
6140 -
6141 - /**
6142 - * Allow CSS to be concatenated into a single jetpack.css file.
6143 - *
6144 - * @since 3.2.0
6145 - *
6146 - * @param bool $do_implode Should CSS be concatenated? Default to true.
6147 - */
6148 - $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6149 -
6150 - // Do not use the imploded file when default behavior was altered through the filter.
6151 - if ( ! $do_implode ) {
6152 - return;
6153 - }
6154 -
6155 - // We do not want to use the imploded file in dev mode, or if not connected.
6156 - if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6157 - if ( ! $travis_test ) {
6158 - return;
6159 - }
6160 - }
6161 -
6162 - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen.
6163 - if ( get_option( 'sharedaddy_disable_resources' ) ) {
6164 - return;
6165 - }
6166 -
6167 - /*
6168 - * Now we assume Jetpack is connected and able to serve the single
6169 - * file.
6170 - *
6171 - * In the future there will be a check here to serve the file locally
6172 - * or potentially from the Jetpack CDN
6173 - *
6174 - * For now:
6175 - * - Enqueue a single imploded css file
6176 - * - Zero out the style_loader_tag for the bundled ones
6177 - * - Be happy, drink scotch
6178 - */
6179 -
6180 - add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6181 -
6182 - $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6183 -
6184 - wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6185 - wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6186 - }
6187 -
6188 - /**
6189 - * Removes styles that are part of concatenated group.
6190 - *
6191 - * @param string $tag Style tag.
6192 - * @param string $handle Style handle.
6193 - *
6194 - * @return string
6195 - */
6196 - public function concat_remove_style_loader_tag( $tag, $handle ) {
6197 - if ( in_array( $handle, $this->concatenated_style_handles, true ) ) {
6198 - $tag = '';
6199 - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6200 - $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6201 - }
6202 - }
6203 -
6204 - return $tag;
6205 - }
6206 -
6207 - /**
6208 6013 * Check the heartbeat data
6209 6014 *
6210 6015 * Organizes the heartbeat data by severity. For example, if the site
6211 6016 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
@@ -6217,9 +6022,23 @@
6217 6022 *
6218 6023 * $return array $filtered_data
6219 6024 */
6220 6025 public static function jetpack_check_heartbeat_data() {
6221 - $raw_data = Jetpack_Heartbeat::generate_stats_array();
6026 + /*
6027 + * Site environment stats (incl. wp-version/php-version checked below) now live in the Connection package,
6028 + * and the IDC stat is contributed by the Connection package's `jetpack_heartbeat_stats_array` filter
6029 + * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6030 + * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6031 + * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6032 + */
6033 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
6034 + ? Heartbeat::get_environment_stats()
6035 + : array();
6036 + $raw_data = array_merge(
6037 + Jetpack_Heartbeat::generate_stats_array(),
6038 + $env_stats,
6039 + array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6040 + );
6222 6041
6223 6042 $good = array();
6224 6043 $caution = array();
6225 6044 $bad = array();
@@ -6285,23 +6104,8 @@
6285 6104 return Jetpack_Options::get_options_for_reset();
6286 6105 }
6287 6106
6288 6107 /**
6289 - * Strip http:// or https:// from a url, replaces forward slash with ::,
6290 - * so we can bring them directly to their site in calypso.
6291 - *
6292 - * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6293 - *
6294 - * @param string $url URL.
6295 - * @return string url without the guff.
6296 - */
6297 - public static function build_raw_urls( $url ) {
6298 - _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6299 -
6300 - return ( new Status() )->get_site_suffix( $url );
6301 - }
6302 -
6303 - /**
6304 6108 * Get the user's meta box order.
6305 6109 *
6306 6110 * @param array $sorted Value for the user's option.
6307 6111 * @return mixed
@@ -6493,13 +6297,20 @@
6493 6297 }
6494 6298 }
6495 6299
6496 6300 /**
6497 - * Returns a boolean for whether backups UI should be displayed or not.
6301 + * Whether UI for backups should be displayed.
6498 6302 *
6303 + * On WPCom platforms this is gated on the backups-self-serve site feature.
6304 + * On self-hosted Jetpack sites it falls back to the jetpack_show_backups filter.
6305 + *
6499 6306 * @return bool Should backups UI be displayed?
6500 6307 */
6501 6308 public static function show_backups_ui() {
6309 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6310 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'backups-self-serve' );
6311 + }
6312 +
6502 6313 /**
6503 6314 * Whether UI for backups should be displayed.
6504 6315 *
6505 6316 * @since 6.5.0
@@ -6509,8 +6320,24 @@
6509 6320 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
6510 6321 }
6511 6322
6512 6323 /**
6324 + * Whether UI for security scanning should be displayed.
6325 + *
6326 + * On WPCom platforms this is gated on the scan-self-serve site feature.
6327 + * On self-hosted Jetpack sites it always returns true.
6328 + *
6329 + * @return bool Should scan UI be displayed?
6330 + */
6331 + public static function show_scan_ui() {
6332 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6333 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'scan-self-serve' );
6334 + }
6335 +
6336 + return true;
6337 + }
6338 +
6339 + /**
6513 6340 * Clean leftoveruser meta.
6514 6341 *
6515 6342 * Delete Jetpack-related user meta when it is no longer needed.
6516 6343 *
@@ -6597,8 +6424,25 @@
6597 6424 _x( 'Increase earnings with WordAds', 'Creator Product Feature', 'jetpack' ),
6598 6425 ),
6599 6426 );
6600 6427
6428 + $products['growth'] = array(
6429 + 'title' => __( 'Jetpack Growth', 'jetpack' ),
6430 + 'slug' => 'jetpack_growth_yearly',
6431 + 'description' => __( 'Essential tools to help you grow your audience, track visitor engagement, and turn leads into loyal customers and advocates.', 'jetpack' ),
6432 + 'show_promotion' => true,
6433 + 'discount_percent' => 50,
6434 + 'included_in_plans' => array( 'complete' ),
6435 + 'features' => array(
6436 + _x( 'Jetpack Social', 'Growth Product Feature', 'jetpack' ),
6437 + _x( 'Jetpack Stats (10K site views, upgradeable)', 'Growth Product Feature', 'jetpack' ),
6438 + _x( 'Unlimited subscriber imports', 'Growth Product Feature', 'jetpack' ),
6439 + _x( 'Earn more from your content', 'Growth Product Feature', 'jetpack' ),
6440 + _x( 'Accept payments with PayPal', 'Growth Product Feature', 'jetpack' ),
6441 + _x( 'Increase earnings with WordAds', 'Growth Product Feature', 'jetpack' ),
6442 + ),
6443 + );
6444 +
6601 6445 $products['scan'] = array(
6602 6446 'title' => __( 'Jetpack Scan', 'jetpack' ),
6603 6447 'slug' => 'jetpack_scan',
6604 6448 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ),
@@ -6737,9 +6581,12 @@
6737 6581 if ( $plugin_file !== 'jetpack/jetpack.php' ) {
6738 6582 return $plugin_meta;
6739 6583 }
6740 6584
6741 - $plugin_meta[] = '<a href="https://wordpress.org/support/plugin/jetpack/reviews/?filter=5" target="_blank" rel="noopener noreferrer" title="' . esc_attr__( 'Rate Jetpack on WordPress.org', 'jetpack' ) . '" style="color: #ffb900">'
6585 + $u = get_current_user_id();
6586 + $site = get_site_url();
6587 +
6588 + $plugin_meta[] = '<a href="https://jetpack.com/redirect?source=jetpack-plugin-review&site=' . esc_attr( $site ) . '&u=' . esc_attr( $u ) . '" target="_blank" rel="noopener noreferrer" title="' . esc_attr__( 'Rate Jetpack on WordPress.org', 'jetpack' ) . '" style="color: #ffb900">'
6742 6589 . str_repeat( '<span class="dashicons dashicons-star-filled" style="font-size: 16px; width:16px; height: 16px"></span>', 5 )
6743 6590 . '</a>';
6744 6591
6745 6592 return $plugin_meta;
@@ -6745,8 +6592,56 @@
6745 6592 return $plugin_meta;
6746 6593 }
6747 6594
6748 6595 /**
6596 + * Lazy instantiation of the Plugin_Tracking object.
6597 + *
6598 + * @since 13.9
6599 + *
6600 + * @return void
6601 + */
6602 + public function initialize_tracking() {
6603 + if ( did_action( 'jetpack_initialize_tracking' ) > 1 ) {
6604 + // Only need to run once.
6605 + return;
6606 + }
6607 +
6608 + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) || static::is_connection_ready() ) {
6609 + ( new Plugin_Tracking() )->init();
6610 + }
6611 + }
6612 +
6613 + /**
6614 + * Run the "initialize tracking" hook.
6615 + *
6616 + * @since 13.9
6617 + */
6618 + public function run_initialize_tracking_action() {
6619 + /**
6620 + * Fires when the tracking needs to be initialized.
6621 + * Doesn't necessarily mean that will actually happen, depends if the 'jetpack_tos_agreed' option is set.
6622 + *
6623 + * @since 13.9
6624 + */
6625 + do_action( 'jetpack_initialize_tracking' );
6626 + }
6627 +
6628 + /**
6629 + * Initialize REST jsonAPI if needed.
6630 + *
6631 + * @return void
6632 + */
6633 + public function maybe_initialize_rest_jsonapi() {
6634 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
6635 + if ( ! empty( $_GET['jsonapi'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) {
6636 + require_once ABSPATH . 'wp-admin/includes/admin.php'; // JSON API relies on WP functionality not autoloaded in REST.
6637 +
6638 + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
6639 + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
6640 + }
6641 + }
6642 +
6643 + /**
6749 6644 * Run plugin post-activation actions if we need to.
6750 6645 *
6751 6646 * @return void
6752 6647 */
@@ -6761,8 +6656,12 @@
6761 6656 if ( ( new Host() )->is_woa_site() ) {
6762 6657 $redirect_url = static::admin_url( 'page=jetpack' );
6763 6658 } elseif ( is_network_admin() ) {
6764 6659 $redirect_url = admin_url( 'network/admin.php?page=jetpack' );
6660 + } elseif ( get_transient( 'my_jetpack_product_activated' ) ) {
6661 + // don't redirect if this is an activation that just came from My Jetpack
6662 + // My Jetpack has its own set of post-activation redirects
6663 + return;
6765 6664 } elseif ( My_Jetpack_Initializer::should_initialize() ) {
6766 6665 $redirect_url = static::admin_url( 'page=my-jetpack' );
6767 6666 } else {
6768 6667 $redirect_url = static::admin_url( 'page=jetpack' );