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