PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | class.jetpack.php +1072 -1188 12.0.3 → 16.3-beta View file →
@@ -7,30 +7,38 @@
7 7 * @package automattic/jetpack
8 8 */
9 9
10 10 use Automattic\Jetpack\Assets;
11 -use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
11 +use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
12 12 use Automattic\Jetpack\Config;
13 +use Automattic\Jetpack\Connection\Authorize_Json_Api;
13 14 use Automattic\Jetpack\Connection\Client;
14 15 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
15 -use Automattic\Jetpack\Connection\Nonce_Handler;
16 16 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
17 17 use Automattic\Jetpack\Connection\Secrets;
18 18 use Automattic\Jetpack\Connection\Tokens;
19 -use Automattic\Jetpack\Connection\Utils as Connection_Utils;
19 +use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
20 20 use Automattic\Jetpack\Constants;
21 21 use Automattic\Jetpack\CookieState;
22 +use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
22 23 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
23 24 use Automattic\Jetpack\Errors;
25 +use Automattic\Jetpack\Feature_Policy;
24 26 use Automattic\Jetpack\Files;
27 +use Automattic\Jetpack\Heartbeat;
25 28 use Automattic\Jetpack\Identity_Crisis;
29 +use Automattic\Jetpack\Import\Main as Import_Main;
26 30 use Automattic\Jetpack\Licensing;
27 31 use Automattic\Jetpack\Modules;
28 32 use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
29 -use Automattic\Jetpack\Partner;
33 +use Automattic\Jetpack\Newsletter\Reader_Link;
30 34 use Automattic\Jetpack\Paths;
35 +use Automattic\Jetpack\Plugin\Deprecate;
31 36 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
37 +use Automattic\Jetpack\Podcast\Podcast;
32 38 use Automattic\Jetpack\Redirect;
39 +use Automattic\Jetpack\Scan_Page\Jetpack_Scan as Scan_Page_Init;
40 +use Automattic\Jetpack\SEO\Initializer as Jetpack_SEO_Initializer;
33 41 use Automattic\Jetpack\Status;
34 42 use Automattic\Jetpack\Status\Host;
35 43 use Automattic\Jetpack\Status\Visitor;
36 44 use Automattic\Jetpack\Sync\Actions as Sync_Actions;
@@ -37,9 +45,14 @@
37 45 use Automattic\Jetpack\Sync\Health;
38 46 use Automattic\Jetpack\Sync\Sender;
39 47 use Automattic\Jetpack\Terms_Of_Service;
40 48 use Automattic\Jetpack\Tracking;
49 +use Automattic\Woocommerce_Analytics;
41 50
51 +if ( ! defined( 'ABSPATH' ) ) {
52 + exit( 0 );
53 +}
54 +
42 55 /*
43 56 Options:
44 57 jetpack_options (array)
45 58 An array of options.
@@ -74,48 +87,8 @@
74 87 */
75 88 public $xmlrpc_server = null;
76 89
77 90 /**
78 - * The handles of styles that are concatenated into jetpack.css.
79 - *
80 - * When making changes to that list, you must also update concat_list in tools/webpack.config.css.js.
81 - *
82 - * @var array The handles of styles that are concatenated into jetpack.css.
83 - */
84 - public $concatenated_style_handles = array(
85 - 'jetpack-carousel-swiper-css',
86 - 'jetpack-carousel',
87 - 'grunion.css',
88 - 'the-neverending-homepage',
89 - 'jetpack_likes',
90 - 'jetpack_related-posts',
91 - 'sharedaddy',
92 - 'jetpack-slideshow',
93 - 'presentations',
94 - 'quiz',
95 - 'jetpack-subscriptions',
96 - 'jetpack-responsive-videos-style',
97 - 'jetpack-social-menu',
98 - 'tiled-gallery',
99 - 'jetpack_display_posts_widget',
100 - 'gravatar-profile-widget',
101 - 'goodreads-widget',
102 - 'jetpack_social_media_icons_widget',
103 - 'jetpack-top-posts-widget',
104 - 'jetpack_image_widget',
105 - 'jetpack-my-community-widget',
106 - 'jetpack-authors-widget',
107 - 'wordads',
108 - 'eu-cookie-law-style',
109 - 'flickr-widget-style',
110 - 'jetpack-search-widget',
111 - 'jetpack-simple-payments-widget-style',
112 - 'jetpack-widget-social-icons-styles',
113 - 'wpcom_instagram_widget',
114 - 'milestone-widget',
115 - );
116 -
117 - /**
118 91 * Contains all assets that have had their URL rewritten to minified versions.
119 92 *
120 93 * @var array
121 94 */
@@ -130,11 +103,8 @@
130 103 'contact-form' => array(
131 104 array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
132 105 array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
133 106 ),
134 - 'custom-css' => array(
135 - array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
136 - ),
137 107 'gravatar-hovercards' => array(
138 108 array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
139 109 ),
140 110 'latex' => array(
@@ -295,21 +265,19 @@
295 265 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
296 266 'Rank Math' => 'seo-by-rank-math/rank-math.php',
297 267 'Slim SEO' => 'slim-seo/slim-seo.php',
298 268 ),
299 - 'lazy-images' => array(
300 - 'Lazy Load' => 'lazy-load/lazy-load.php',
301 - 'BJ Lazy Load' => 'bj-lazy-load/bj-lazy-load.php',
302 - 'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
303 - ),
304 269 );
305 270
306 271 /**
307 272 * Plugins for which we turn off our Facebook OG Tags implementation.
308 273 *
309 - * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
310 - * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
274 + * Note: the following plugins automatically deactivate Jetpack's Open
275 + * Graph tags via filter when their Social Meta modules are active:
311 276 *
277 + * - All in One SEO Pack, All in one SEO Pack Pro
278 + * - WordPress SEO by Yoast, WordPress SEO Premium by Yoast
279 + *
312 280 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
313 281 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
314 282 *
315 283 * @var array Array of plugin slugs.
@@ -351,8 +319,10 @@
351 319 'wp-caregiver/wp-caregiver.php', // WP Caregiver.
352 320 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta.
353 321 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
354 322 'wp-ogp/wp-ogp.php', // WP-OGP.
323 + 'wp-seopress/seopress.php', // SEOPress.
324 + 'wp-seopress-pro/seopress-pro.php', // SEOPress Pro.
355 325 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
356 326 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button.
357 327 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox.
358 328 'seo-by-rank-math/rank-math.php', // Rank Math.
@@ -419,8 +389,10 @@
419 389
420 390 /**
421 391 * Verified data for JSON authorization request
422 392 *
393 + * @deprecated 13.4
394 + *
423 395 * @var array
424 396 */
425 397 public $json_api_authorization_request = array();
426 398
@@ -461,8 +433,16 @@
461 433 */
462 434 public static $instance = false;
463 435
464 436 /**
437 + * Resolved answer for `is_premium_analytics_enabled()`, or null before the first call.
438 + *
439 + * @since 16.1
440 + * @var bool|null
441 + */
442 + private static $premium_analytics_enabled = null;
443 +
444 + /**
465 445 * Singleton
466 446 *
467 447 * @static
468 448 */
@@ -482,17 +462,21 @@
482 462 public function plugin_upgrade() {
483 463 if ( self::is_connection_ready() ) {
484 464 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
485 465 if ( JETPACK__VERSION !== $version ) {
486 - // Prevent multiple upgrades at once - only a single process should trigger
487 - // an upgrade to avoid stampedes.
488 - if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
489 - return;
466 + // Prevent multiple upgrades at once - only a single process should trigger an upgrade to avoid stampedes.
467 + if ( wp_using_ext_object_cache() ) {
468 + if ( true !== wp_cache_add( self::$plugin_upgrade_lock_key, 1, 'transient', 10 ) ) {
469 + return;
470 + }
471 + } else {
472 + if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
473 + return;
474 + }
475 +
476 + set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
490 477 }
491 478
492 - // Set a short lock to prevent multiple instances of the upgrade.
493 - set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
494 -
495 479 // check which active modules actually exist and remove others from active_modules list.
496 480 $unfiltered_modules = self::get_active_modules();
497 481 $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
498 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
@@ -498,9 +482,9 @@
498 482 if ( array_diff( $unfiltered_modules, $modules ) ) {
499 483 self::update_active_modules( $modules );
500 484 }
501 485
502 - add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
486 + self::register_upgrade_init_hooks();
503 487
504 488 // Upgrade to 4.3.0.
505 489 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
506 490 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
@@ -555,8 +539,16 @@
555 539 Jetpack_Options::delete_option( 'autoupdate_plugins' );
556 540 } // Should we have some type of fallback if something fails here?
557 541 }
558 542
543 + // Set the newsletter send default option for existing sites.
544 + if ( false === get_option( 'wpcom_newsletter_send_default' ) ) {
545 + add_option( 'wpcom_newsletter_send_default', 1 );
546 + }
547 +
548 + // Its handler went with the Recommendations assistant.
549 + wp_clear_scheduled_hook( 'jetpack_recommend_videopress' );
550 +
559 551 if ( did_action( 'wp_loaded' ) ) {
560 552 self::upgrade_on_load();
561 553 } else {
562 554 add_action(
@@ -590,9 +582,8 @@
590 582 }
591 583
592 584 if (
593 585 class_exists( 'Jetpack_Sitemap_Manager' )
594 - && version_compare( JETPACK__VERSION, '5.3', '>=' )
595 586 ) {
596 587 do_action( 'jetpack_sitemaps_purge_data' );
597 588 }
598 589
@@ -607,9 +598,9 @@
607 598 * Also fires Action hooks for each newly activated and deactivated module.
608 599 *
609 600 * @param array $modules Array of active modules to be saved in options.
610 601 *
611 - * @return $success bool true for success, false for failure.
602 + * @return bool $success true for success, false for failure.
612 603 */
613 604 public static function update_active_modules( $modules ) {
614 605 return ( new Modules() )->update_active( $modules );
615 606 }
@@ -639,10 +630,10 @@
639 630 sort( $taken_priorities );
640 631
641 632 $first_priority = array_shift( $taken_priorities );
642 633
643 - if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
644 - $new_priority = - PHP_INT_MAX;
634 + if ( $first_priority <= PHP_INT_MIN ) {
635 + $new_priority = PHP_INT_MIN;
645 636 } else {
646 637 $new_priority = $first_priority - 1;
647 638 }
648 639
@@ -663,32 +654,17 @@
663 654 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
664 655 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
665 656 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
666 657
667 - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
668 -
669 - add_filter(
670 - 'jetpack_signature_check_token',
671 - array( __CLASS__, 'verify_onboarding_token' ),
672 - 10,
673 - 3
674 - );
675 -
676 658 /**
677 659 * Prepare Gutenberg Editor functionality
660 + *
661 + * The hooks previously here have been moved to modules/blocks.php but leaving this here pending
662 + * a longer investigation to see if code is expecting the Gutenberg class to always be available.
678 663 */
679 664 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
680 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
681 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
682 - add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 );
683 - add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
684 -
685 665 add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
686 666
687 - // Unlink user before deleting the user from WP.com.
688 - add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 );
689 - add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 );
690 -
691 667 add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
692 668
693 669 add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
694 670 add_action( 'login_init', array( $this, 'login_init' ) );
@@ -695,8 +671,13 @@
695 671
696 672 // Set up the REST authentication hooks.
697 673 Connection_Rest_Authentication::init();
698 674
675 + // Register Jetpack-specific connection tests (sync health, etc.) with the connection
676 + // package's health test suite. This runs on all requests (not just admin), because
677 + // the connection/test REST endpoint can be called outside admin context.
678 + add_action( 'jetpack_connection_tests_loaded', array( $this, 'register_jetpack_connection_tests' ) );
679 +
699 680 add_action( 'admin_init', array( $this, 'admin_init' ) );
700 681 add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
701 682
702 683 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
@@ -710,12 +691,8 @@
710 691
711 692 // Returns HTTPS support status.
712 693 add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
713 694
714 - add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
715 -
716 - add_action( 'wp_ajax_jetpack_recommendations_banner', array( 'Jetpack_Recommendations_Banner', 'ajax_callback' ) );
717 -
718 695 add_action( 'wp_loaded', array( $this, 'register_assets' ) );
719 696
720 697 /**
721 698 * These actions run checks to load additional files.
@@ -733,29 +710,8 @@
733 710
734 711 add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
735 712 add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
736 713
737 - require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php';
738 - $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() );
739 - add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) );
740 -
741 - /*
742 - * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
743 - * We should make sure to only do this for front end links.
744 - */
745 - if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
746 - add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
747 - add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
748 -
749 - /*
750 - * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
751 - * so they point moderation links on emails to Calypso.
752 - */
753 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/functions.wp-notify.php';
754 - add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
755 - add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
756 - }
757 -
758 714 add_action(
759 715 'plugins_loaded',
760 716 function () {
761 717 if ( User_Agent_Info::is_mobile_app() ) {
@@ -764,20 +720,12 @@
764 720 }
765 721 );
766 722
767 723 // Update the site's Jetpack plan and products from API on heartbeats.
768 - add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
724 + add_action( 'jetpack_heartbeat', array( Jetpack_Plan::class, 'refresh_from_wpcom' ) );
769 725
770 - /**
771 - * This is the hack to concatenate all css files into one.
772 - * For description and reasoning see the implode_frontend_css method.
773 - *
774 - * Super late priority so we catch all the registered styles.
775 - */
776 - if ( ! is_admin() ) {
777 - add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first.
778 - add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`.
779 - }
726 + // The Connection package fetches the site record for `jetpack/v4/site`; reuse it to refresh the plan.
727 + add_action( 'jetpack_site_data_fetched', array( Jetpack_Plan::class, 'update_from_site_record' ) );
780 728
781 729 // Actually push the stats on shutdown.
782 730 if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
783 731 add_action( 'shutdown', array( $this, 'push_stats' ) );
@@ -785,8 +733,10 @@
785 733
786 734 // After a successful connection.
787 735 add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) );
788 736 add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) );
737 + add_action( 'jetpack_site_registered', array( Reader_Link::class, 'activate_on_connection' ), 9 );
738 + add_action( 'jetpack_site_registered', array( \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::class, 'discard_credentials' ) );
789 739
790 740 // Actions for Manager::authorize().
791 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
792 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
@@ -791,8 +741,9 @@
791 741 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
792 742 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
793 743 add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
794 744
745 + Jetpack_Client_Server::init();
795 746 add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) );
796 747 add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) );
797 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
798 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
@@ -797,9 +748,9 @@
797 748 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
798 749 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
799 750
800 751 // Filters for the Manager::get_token() urls and request body.
801 - add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
752 + add_filter( 'jetpack_token_redirect_url', array( Authorize_Redirect::class, 'filter_connect_redirect_url' ) );
802 753 add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
803 754
804 755 // Filter for the `jetpack/v4/connection/data` API response.
805 756 add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) );
@@ -820,18 +771,189 @@
820 771 // Make resources use static domain when possible.
821 772 add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) );
822 773
823 774 // Validate the domain names in Jetpack development versions.
824 - add_action( 'jetpack_pre_register', array( get_called_class(), 'registration_check_domains' ) );
775 + add_action( 'jetpack_pre_register', array( static::class, 'registration_check_domains' ) );
825 776
826 777 // Register product descriptions for partner coupon usage.
827 778 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
828 779
829 - // Actions for conditional recommendations.
830 - add_action( 'plugins_loaded', array( 'Jetpack_Recommendations', 'init_conditional_recommendation_actions' ) );
780 + // Add 5-star
781 + add_filter( 'plugin_row_meta', array( $this, 'add_5_star_review_link' ), 10, 2 );
782 + add_action( 'init', array( Deprecate::class, 'instance' ) );
783 +
784 + // Register Jetpack module management abilities (WordPress Abilities API, WP 6.9+).
785 + \Automattic\Jetpack\Plugin\Abilities\Modules_Abilities::init();
786 +
787 + // Register Connection abilities (WordPress Abilities API, WP 6.9+). Scoped to the
788 + // Jetpack plugin for now: the Connection package no longer auto-wires these, so
789 + // connection-only consumers (Boost, Protect, Search, etc.) do not register them yet.
790 + \Automattic\Jetpack\Connection\Abilities\Connection_Abilities::init();
831 791 }
832 792
833 793 /**
794 + * Whether the current request should eagerly initialize the admin/REST-only
795 + * packages (the Import package and My Jetpack) now, at `plugins_loaded` time.
796 + *
797 + * Returns true for admin, cron, POST, and WP-CLI requests — the contexts,
798 + * knowable this early, where those packages have work to do. Returns false
799 + * for a plain front-end GET *and* for a REST request: the two can't be told
800 + * apart yet (this runs before `rest_api_init`), so callers defer the REST
801 + * case by initializing the package on `rest_api_init` instead, while a plain
802 + * page view never fires that hook and so loads nothing. This keeps
803 + * admin/REST-only PHP out of opcache on the front-end GET hot path.
804 + *
805 + * No in-repo code depends on the deferral. The one externally observable
806 + * change is timing: the packages' documented init hooks
807 + * (`jetpack_import_initialized`, `jetpack_feature_import_enabled`, and
808 + * `my_jetpack_init`) no longer fire on a plain front-end GET — they fire on
809 + * the admin, cron, POST, WP-CLI, and REST requests where the packages load.
810 + *
811 + * @return bool
812 + */
813 + private static function should_eager_load_packages() {
814 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) );
815 + $is_wp_cli = Constants::is_true( 'WP_CLI' );
816 +
817 + return is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli;
818 + }
819 +
820 + /**
821 + * Configure the Import package from a deferred hook.
822 + *
823 + * The eager path uses Config::ensure( 'import' ), but the deferred REST path
824 + * runs after Config::on_plugins_loaded() has already processed its feature
825 + * flags, so it needs a hookable bootstrap callback. Preserve Config's
826 + * feature-enabled action for hook consumers.
827 + *
828 + * @since 16.0
829 + *
830 + * @return void
831 + */
832 + public static function configure_import_package() {
833 + if ( class_exists( Import_Main::class ) ) {
834 + Import_Main::configure();
835 +
836 + if ( ! did_action( 'jetpack_feature_import_enabled' ) ) {
837 + do_action( 'jetpack_feature_import_enabled' );
838 + }
839 + }
840 + }
841 +
842 + /**
843 + * Enable the bundled Backup dashboard.
844 + *
845 + * The standalone plugin initializes the package first, so with both active this is a no-op.
846 + *
847 + * @return void
848 + */
849 + public static function configure_backup_package() {
850 + // Not offered on multisite, which the Backup package does not support, or without a
851 + // connected owner, since buying and managing backups needs a linked account.
852 + if ( is_multisite() || ! self::is_connection_ready() || ! self::connection()->has_connected_owner() ) {
853 + return;
854 + }
855 +
856 + if ( ! self::is_module_active( 'backup' ) ) {
857 + return;
858 + }
859 +
860 + /**
861 + * Filters whether the Jetpack plugin offers its bundled Backup dashboard.
862 + *
863 + * Resolved at the earliest `plugins_loaded` priority, so hook it from a mu-plugin.
864 + *
865 + * @since 16.3
866 + *
867 + * @param bool $enabled Whether to initialize the bundled Backup dashboard. Default true.
868 + */
869 + if ( ! apply_filters( 'jetpack_backup_dashboard_enabled', true ) ) {
870 + return;
871 + }
872 +
873 + $backup = 'Automattic\\Jetpack\\Backup\\V0005\\Jetpack_Backup';
874 +
875 + // An older package would take over the connection (see Jetpack_Backup::DEFAULT_INIT_OPTIONS);
876 + // only the standalone plugin ships one that old, and it draws its own menu.
877 + if ( ! class_exists( $backup ) || ! defined( $backup . '::DEFAULT_INIT_OPTIONS' ) ) {
878 + return;
879 + }
880 +
881 + $backup::initialize( array( 'manage_connection' => false ) );
882 + }
883 +
884 + /**
885 + * Whether the bundled Stats v2 dashboard is enabled.
886 + *
887 + * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
888 + * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
889 + * menu alongside the existing Stats UI; it never replaces or hides the
890 + * legacy Stats menu, admin-bar entries, post-list column, or WP dashboard
891 + * widget. The Stats module's tracking is unaffected either way, and Stats v2
892 + * reads what that module collects, so while the module is off the plugin
893 + * answers false here before even reading the flag.
894 + *
895 + * The package has to be loadable for this to be true, so a site with the
896 + * flag on but a missing package answers false here and never adds the
897 + * Stats v2 menu (a warning is logged instead).
898 + *
899 + * @since 16.1
900 + *
901 + * @return bool
902 + */
903 + public static function is_premium_analytics_enabled() {
904 + if ( null !== self::$premium_analytics_enabled ) {
905 + return self::$premium_analytics_enabled;
906 + }
907 +
908 + if ( ! self::is_module_active( 'stats' ) ) {
909 + self::$premium_analytics_enabled = false;
910 + return false;
911 + }
912 +
913 + /**
914 + * Filters whether the bundled Premium Analytics dashboard is enabled.
915 + *
916 + * Resolved once, from `Jetpack::configure()` on `plugins_loaded`, and only
917 + * while the Stats module is active. Register this from a mu-plugin or a
918 + * plugin's main file — a callback added on `plugins_loaded` or later runs
919 + * too late to be seen.
920 + *
921 + * @since 16.1
922 + *
923 + * @param bool $enabled Defaults to the `jetpack_premium_analytics_enabled` option (false).
924 + */
925 + $flag = (bool) apply_filters( 'jetpack_premium_analytics_enabled', (bool) get_option( 'jetpack_premium_analytics_enabled' ) );
926 +
927 + self::$premium_analytics_enabled = $flag && class_exists( 'Automattic\Jetpack\PremiumAnalytics\Analytics' );
928 +
929 + if ( $flag && ! self::$premium_analytics_enabled ) {
930 + wp_trigger_error(
931 + __METHOD__,
932 + 'The jetpack_premium_analytics_enabled flag is on but the Premium Analytics package is not loadable; keeping the Stats UI in place.'
933 + );
934 + }
935 +
936 + return self::$premium_analytics_enabled;
937 + }
938 +
939 + /**
940 + * Expose the setting that turns the Premium Analytics dashboard on and off.
941 + *
942 + * Deliberately not behind is_premium_analytics_enabled(): this is the setting that flips that
943 + * check, so it has to answer while the dashboard is still off.
944 + *
945 + * @since 16.2
946 + *
947 + * @return void
948 + */
949 + public static function register_premium_analytics_enablement_setting() {
950 + if ( class_exists( 'Automattic\Jetpack\PremiumAnalytics\Enablement_Setting' ) ) {
951 + \Automattic\Jetpack\PremiumAnalytics\Enablement_Setting::register();
952 + }
953 + }
954 +
955 + /**
834 956 * Before everything else starts getting initalized, we need to initialize Jetpack using the
835 957 * Config object.
836 958 */
837 959 public function configure() {
@@ -838,16 +960,12 @@
838 960 $config = new Config();
839 961
840 962 foreach (
841 963 array(
842 - 'blaze',
843 964 'jitm',
844 965 'sync',
966 + 'account_protection',
845 967 'waf',
846 - 'videopress',
847 - 'stats',
848 - 'stats_admin',
849 - 'import',
850 968 )
851 969 as $feature
852 970 ) {
853 971 $config->ensure( $feature );
@@ -852,8 +970,110 @@
852 970 ) {
853 971 $config->ensure( $feature );
854 972 }
855 973
974 + // Enable the VideoPress admin UI (the "Jetpack > VideoPress" dashboard) inside the
975 + // Jetpack plugin, mirroring the standalone Jetpack VideoPress plugin. The dashboard
976 + // only renders when the VideoPress module is active (Status::is_active()); when it
977 + // is not, the menu item links to the My Jetpack interstitial to activate it.
978 + $config->ensure( 'videopress', array( 'admin_ui' => true ) );
979 +
980 + // The Backup dashboard is only an admin menu and REST routes, so it is deferred like Import below.
981 + if ( self::should_eager_load_packages() ) {
982 + self::configure_backup_package();
983 + } else {
984 + add_action( 'rest_api_init', array( __CLASS__, 'configure_backup_package' ), 0 );
985 + }
986 +
987 + /*
988 + * The Import package only registers `jetpack/v4/import` REST routes — it
989 + * does nothing when rendering a front-end page — so gate its `ensure()`
990 + * to keep its PHP out of opcache on the front-end GET hot path. It still
991 + * loads on admin, cron, POST, and WP-CLI requests, and on `rest_api_init`
992 + * for REST: a REST request can't be identified yet at `plugins_loaded`
993 + * (this runs before `Config::on_plugins_loaded`, and `rest_api_init`
994 + * fires later), so it is initialized directly when that hook fires, while
995 + * a plain page view never fires it and so loads nothing.
996 + *
997 + * JITM stays eager (above): unlike Import, its `register()` adds a
998 + * `jetpack_sync_before_send_updated_option` filter that records the
999 + * `jetpack_last_plugin_sync` transient, and a Jetpack Sync send can fire
1000 + * on a plain front-end GET — including the dedicated-sync `spawn-sync`
1001 + * GET, which runs on `init` and exits before `rest_api_init`. Deferring
1002 + * JITM would skip that bookkeeping and leave its message cache stale after
1003 + * a plugin change, so it loads on every request as before.
1004 + */
1005 + if ( self::should_eager_load_packages() ) {
1006 + $config->ensure( 'import' );
1007 + } else {
1008 + add_action(
1009 + 'rest_api_init',
1010 + array( __CLASS__, 'configure_import_package' ),
1011 + 0
1012 + );
1013 + }
1014 +
1015 + /*
1016 + * The Stats and Stats Admin packages only do work when the Stats module
1017 + * is active (the front-end tracking pixel) or on wp-admin, REST, cron,
1018 + * POST, and WP-CLI requests: the Stats dashboard page, the stats /
1019 + * stats-app REST endpoints (which the block editor also calls for
1020 + * email-open rates), the transient-cleanup cron, the connection
1021 + * package's package-version tracker (which runs on POSTs and reads the
1022 + * `jetpack_package_versions` filter that Stats registers), and CLI
1023 + * introspection such as the heartbeat inspector. On a plain front-end
1024 + * GET page view with the module off they are inert: the pixel
1025 + * short-circuits on `Stats\Main::should_track()` and every other entry
1026 + * point only hooks rest_api_init, admin, cron, the POST-only tracker, or
1027 + * is reached through WP-CLI.
1028 + * Skip loading them — and eagerly constructing the Stats Admin REST
1029 + * controller — on that hot path to keep their PHP out of opcache, but
1030 + * keep loading them everywhere else exactly as before so the stats REST
1031 + * permission mapping (view_stats, registered by Stats\Main), the
1032 + * editor's stats-app calls, the cleanup cron, and the package-version
1033 + * tracker are all unchanged.
1034 + *
1035 + * REST requests are not yet identifiable here (REST_REQUEST is defined
1036 + * after plugins_loaded), so defer those to rest_api_init. Call the
1037 + * package initializers directly rather than `$config->ensure()`: ensure()
1038 + * only flags a feature for `Config::on_plugins_loaded()` (plugins_loaded
1039 + * priority 2), which has already run by the time rest_api_init fires.
1040 + * Priority 0 runs before each package's own priority-10 route
1041 + * registration, so their routes still register within the same dispatch.
1042 + * A plain page view never fires rest_api_init, so the packages stay
1043 + * unloaded there. See JETPACK-1747.
1044 + */
1045 + $is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'];
1046 + $is_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
1047 +
1048 + if ( self::is_module_active( 'stats' ) || is_admin() || wp_doing_cron() || $is_post_request || $is_wp_cli ) {
1049 + $config->ensure( 'stats' );
1050 + $config->ensure( 'stats_admin' );
1051 + } else {
1052 + add_action(
1053 + 'rest_api_init',
1054 + static function () {
1055 + if ( class_exists( 'Automattic\Jetpack\Stats\Main' ) ) {
1056 + \Automattic\Jetpack\Stats\Main::init();
1057 + }
1058 + if ( class_exists( 'Automattic\Jetpack\Stats_Admin\Main' ) ) {
1059 + \Automattic\Jetpack\Stats_Admin\Main::init();
1060 + }
1061 + },
1062 + 0
1063 + );
1064 + }
1065 +
1066 + // Stats v2 (WOOA7S-1595). Unlike Stats above it cannot be deferred when enabled — see
1067 + // Analytics::init() for why, and for why it takes no menu_title here.
1068 + if ( self::is_premium_analytics_enabled() ) {
1069 + \Automattic\Jetpack\PremiumAnalytics\Analytics::init();
1070 + }
1071 +
1072 + // Outside the check above on purpose — see Enablement_Setting. Deferred like Stats, to keep
1073 + // the autoload off the front-end hot path.
1074 + add_action( 'rest_api_init', array( __CLASS__, 'register_premium_analytics_enablement_setting' ), 0 );
1075 +
856 1076 $config->ensure(
857 1077 'connection',
858 1078 array(
859 1079 'slug' => 'jetpack',
@@ -871,12 +1091,8 @@
871 1091 );
872 1092
873 1093 $config->ensure( 'search' );
874 1094
875 - if ( defined( 'ENABLE_WORDADS_SHARED_UI' ) && ENABLE_WORDADS_SHARED_UI ) {
876 - $config->ensure( 'wordads' );
877 - }
878 -
879 1095 if ( ! $this->connection_manager ) {
880 1096 $this->connection_manager = new Connection_Manager( 'jetpack' );
881 1097 }
882 1098
@@ -884,8 +1100,10 @@
884 1100 if ( $modules->is_active( 'publicize' ) && $this->connection_manager->has_connected_user() ) {
885 1101 $config->ensure( 'publicize' );
886 1102 }
887 1103
1104 + add_action( 'jetpack_initialize_tracking', array( $this, 'initialize_tracking' ) );
1105 +
888 1106 /*
889 1107 * Load things that should only be in Network Admin.
890 1108 *
891 1109 * For now blow away everything else until a more full
@@ -896,10 +1114,13 @@
896 1114 $network = Jetpack_Network::init();
897 1115 $network->set_connection( $this->connection_manager );
898 1116 }
899 1117
900 - if ( self::is_connection_ready() ) {
1118 + $is_connection_ready = self::is_connection_ready();
1119 +
1120 + if ( $is_connection_ready ) {
901 1121 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
1122 + $this->run_initialize_tracking_action();
902 1123
903 1124 Jetpack_Heartbeat::init();
904 1125 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
905 1126 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
@@ -904,8 +1125,19 @@
904 1125 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
905 1126 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
906 1127 Jetpack_Search_Performance_Logger::init();
907 1128 }
1129 + } else {
1130 + add_action( 'jetpack_agreed_to_terms_of_service', array( $this, 'run_initialize_tracking_action' ) );
1131 + add_action( 'rest_api_init', array( $this, 'run_initialize_tracking_action' ) );
1132 + add_filter(
1133 + 'xmlrpc_methods',
1134 + function ( $methods ) {
1135 + $this->run_initialize_tracking_action();
1136 + return $methods;
1137 + },
1138 + 1
1139 + );
908 1140 }
909 1141
910 1142 // Initialize remote file upload request handlers.
911 1143 $this->add_remote_request_handlers();
@@ -912,23 +1144,13 @@
912 1144
913 1145 /*
914 1146 * Enable enhanced handling of previewing sites in Calypso
915 1147 */
916 - if ( self::is_connection_ready() ) {
1148 + if ( $is_connection_ready ) {
917 1149 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
918 1150 add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
919 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
920 - add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
1151 + add_action( 'rest_api_init', array( $this, 'maybe_initialize_rest_jsonapi' ) );
921 1152 }
922 -
923 - if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
924 - add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
925 - } else {
926 - /**
927 - * Initialize tracking right after the user agrees to the terms of service.
928 - */
929 - add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
930 - }
931 1153 }
932 1154
933 1155 /**
934 1156 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
@@ -936,13 +1158,58 @@
936 1158 *
937 1159 * @action plugins_loaded
938 1160 */
939 1161 public function late_initialization() {
940 - add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
1162 + add_action( 'after_setup_theme', array( 'Jetpack', 'load_modules' ), -2 );
941 1163
942 - Partner::init();
943 - My_Jetpack_Initializer::init();
1164 + /*
1165 + * My Jetpack is a wp-admin dashboard. Its Initializer::init() only wires
1166 + * up admin-menu, admin_init, and rest_api_init surfaces — and eagerly
1167 + * loads every product class (backup, boost, protect, …) just to register
1168 + * admin plugin-action links — so none of it is needed on a plain
1169 + * front-end GET page view. (The pieces that do immediate work, e.g.
1170 + * Connection REST authentication and Licensing, are already initialized
1171 + * unconditionally in Jetpack's constructor, so they are unaffected here.)
1172 + *
1173 + * Gate the call to the request types where My Jetpack actually does work.
1174 + * REST can't be detected yet at plugins_loaded, so initialize on
1175 + * rest_api_init for that branch; a plain page view never fires it, so My
1176 + * Jetpack stays unloaded there.
1177 + */
1178 + if ( self::should_eager_load_packages() ) {
1179 + My_Jetpack_Initializer::init();
1180 + } else {
1181 + add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1182 + }
944 1183
1184 + Scan_Page_Init::initialize();
1185 + Jetpack_SEO_Initializer::init();
1186 +
1187 + if ( ( new Modules() )->is_active( 'podcast' ) ) {
1188 + Podcast::init();
1189 + }
1190 +
1191 + /*
1192 + * Initialize Boost Speed Score. It only does work on REST requests (the
1193 + * dashboard speed-score endpoints) and on a few Jetpack Boost lifecycle
1194 + * actions, so defer constructing it — and loading the boost-speed-score
1195 + * package classes — until one of those hooks actually fires instead of on
1196 + * every request. Priority 0 ensures the object's own callbacks (added in
1197 + * its constructor at the default priority) still run for the firing hook.
1198 + */
1199 + $initialize_speed_score = static function () {
1200 + static $initialized = false;
1201 + if ( $initialized ) {
1202 + return;
1203 + }
1204 + $initialized = true;
1205 + new Speed_Score( array(), 'jetpack-dashboard' );
1206 + };
1207 + add_action( 'rest_api_init', $initialize_speed_score, 0 );
1208 + add_action( 'jetpack_boost_deactivate', $initialize_speed_score, 0 );
1209 + add_action( 'jetpack_boost_environment_changed', $initialize_speed_score, 0 );
1210 + add_action( 'handle_environment_change', $initialize_speed_score, 0 );
1211 +
945 1212 /**
946 1213 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
947 1214 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
948 1215 *
@@ -980,8 +1247,10 @@
980 1247
981 1248 /**
982 1249 * Redirect edit post links to Calypso.
983 1250 *
1251 + * @deprecated since 13.9
1252 + *
984 1253 * @param string $default_url Post edit URL.
985 1254 * @param int $post_id Post ID.
986 1255 *
987 1256 * @return string
@@ -986,8 +1255,10 @@
986 1255 *
987 1256 * @return string
988 1257 */
989 1258 public function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1259 + _deprecated_function( __METHOD__, '13.9' );
1260 +
990 1261 $post = get_post( $post_id );
991 1262
992 1263 if ( empty( $post ) ) {
993 1264 return $default_url;
@@ -1018,13 +1289,17 @@
1018 1289
1019 1290 /**
1020 1291 * Redirect edit comment links to Calypso.
1021 1292 *
1293 + * @deprecated since 13.9
1294 + *
1022 1295 * @param string $url Comment edit URL.
1023 1296 *
1024 1297 * @return string
1025 1298 */
1026 1299 public function point_edit_comment_links_to_calypso( $url ) {
1300 + _deprecated_function( __METHOD__, '13.9' );
1301 +
1027 1302 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1028 1303 $query_args = null;
1029 1304 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1030 1305
@@ -1043,30 +1318,16 @@
1043 1318 *
1044 1319 * @return array list of callables.
1045 1320 */
1046 1321 public function filter_sync_callable_whitelist( $callables ) {
1047 -
1048 1322 // Jetpack Functions.
1049 1323 $jetpack_callables = array(
1050 1324 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1051 1325 'updates' => array( 'Jetpack', 'get_updates' ),
1052 1326 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1327 + 'theme_styles' => array( 'Automattic\\Jetpack\\Plugin\\Theme_Styles_Sync', 'get_theme_styles' ),
1053 1328 );
1054 - $callables = array_merge( $callables, $jetpack_callables );
1055 -
1056 - // Jetpack_SSO_Helpers.
1057 - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1058 - $sso_helpers = array(
1059 - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1060 - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1061 - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1062 - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1063 - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1064 - );
1065 - $callables = array_merge( $callables, $sso_helpers );
1066 - }
1067 -
1068 - return $callables;
1329 + return array_merge( $callables, $jetpack_callables );
1069 1330 }
1070 1331
1071 1332 /**
1072 1333 * Extend Sync multisite callables with Jetpack Plugin functions.
@@ -1091,41 +1352,8 @@
1091 1352 return $callables;
1092 1353 }
1093 1354
1094 1355 /**
1095 - * Deprecated
1096 - * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1097 - *
1098 - * @param array $params The action parameters.
1099 - *
1100 - * @deprecated since 9.8.
1101 - */
1102 - public function jetpack_track_last_sync_callback( $params ) {
1103 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1104 - return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1105 - }
1106 -
1107 - /**
1108 - * Jetpack Connection banner callback function.
1109 - *
1110 - * @return void
1111 - */
1112 - public function jetpack_connection_banner_callback() {
1113 - check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1114 -
1115 - // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1116 - if (
1117 - isset( $_REQUEST['dismissBanner'] ) &&
1118 - ! Jetpack_Connection_Banner::force_display()
1119 - ) {
1120 - Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1121 - wp_send_json_success();
1122 - }
1123 -
1124 - wp_die();
1125 - }
1126 -
1127 - /**
1128 1356 * If there are any stats that need to be pushed, but haven't been, push them now.
1129 1357 */
1130 1358 public function push_stats() {
1131 1359 if ( ! empty( $this->stats ) ) {
@@ -1140,16 +1368,8 @@
1140 1368 * @param string $cap Capability name.
1141 1369 */
1142 1370 public function jetpack_custom_caps( $caps, $cap ) {
1143 1371 switch ( $cap ) {
1144 - case 'jetpack_manage_modules':
1145 - case 'jetpack_activate_modules':
1146 - case 'jetpack_deactivate_modules':
1147 - $caps = array( 'manage_options' );
1148 - break;
1149 - case 'jetpack_configure_modules':
1150 - $caps = array( 'manage_options' );
1151 - break;
1152 1372 case 'jetpack_manage_autoupdates':
1153 1373 $caps = array(
1154 1374 'manage_options',
1155 1375 'update_plugins',
@@ -1167,9 +1387,9 @@
1167 1387 if ( $is_offline_mode ) {
1168 1388 $caps = array( 'manage_options' );
1169 1389 break;
1170 1390 } else {
1171 - $caps = array( 'read' );
1391 + $caps = array( 'edit_posts' );
1172 1392 }
1173 1393 break;
1174 1394 }
1175 1395 return $caps;
@@ -1239,12 +1459,8 @@
1239 1459 * Register the social logos
1240 1460 */
1241 1461 require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1242 1462 jetpack_register_social_logos();
1243 -
1244 - if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1245 - wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1246 - }
1247 1463 }
1248 1464
1249 1465 /**
1250 1466 * Guess locale from language code.
@@ -1332,9 +1548,9 @@
1332 1548 }
1333 1549 /**
1334 1550 * Does the network allow admins to add new users.
1335 1551 *
1336 - * @return boolian
1552 + * @return bool
1337 1553 */
1338 1554 public static function network_add_new_users() {
1339 1555 return (bool) get_site_option( 'add_new_users' );
1340 1556 }
@@ -1341,9 +1557,9 @@
1341 1557 /**
1342 1558 * File upload psace left per site in MB.
1343 1559 * -1 means NO LIMIT.
1344 1560 *
1345 - * @return number
1561 + * @return int
1346 1562 */
1347 1563 public static function network_site_upload_space() {
1348 1564 // value in MB.
1349 1565 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
@@ -1360,9 +1576,9 @@
1360 1576
1361 1577 /**
1362 1578 * Maximum file upload size set by the network.
1363 1579 *
1364 - * @return number
1580 + * @return int
1365 1581 */
1366 1582 public static function network_max_upload_file_size() {
1367 1583 // value in KB.
1368 1584 return get_site_option( 'fileupload_maxk', 300 );
@@ -1521,9 +1737,9 @@
1521 1737 }
1522 1738 return 0;
1523 1739 }
1524 1740
1525 -// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1741 + // phpcs:disable WordPress.WP.CapitalPDangit.MisspelledInComment
1526 1742 /**
1527 1743 * Gets updates and stores in jetpack_updates.
1528 1744 *
1529 1745 * The jetpack_updates option is saved in the following schema:
@@ -1539,8 +1755,9 @@
1539 1755 *
1540 1756 * @return array
1541 1757 */
1542 1758 public static function get_updates() {
1759 + $updates = array();
1543 1760 $update_data = wp_get_update_data();
1544 1761
1545 1762 // Stores the individual update counts as well as the total count.
1546 1763 if ( isset( $update_data['counts'] ) ) {
@@ -1553,11 +1770,11 @@
1553 1770 if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1554 1771 $updates['wp_update_version'] = $cur->current;
1555 1772 }
1556 1773 }
1557 - return isset( $updates ) ? $updates : array();
1774 + return $updates;
1558 1775 }
1559 - // phpcs:enable
1776 + // phpcs:enable WordPress.WP.CapitalPDangit.MisspelledInComment
1560 1777
1561 1778 /**
1562 1779 * Get update details for core, plugins, and themes.
1563 1780 *
@@ -1615,44 +1832,8 @@
1615 1832 return apply_filters( 'jetpack_is_connection_ready', self::connection()->is_connected(), self::connection() );
1616 1833 }
1617 1834
1618 1835 /**
1619 - * Deprecated: Is Jetpack in development (offline) mode?
1620 - *
1621 - * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1622 - * and themes still use it, and we do not want to flood them with notices.
1623 - *
1624 - * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1625 - *
1626 - * @deprecated since 8.0.
1627 - */
1628 - public static function is_development_mode() {
1629 - _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1630 - return ( new Status() )->is_offline_mode();
1631 - }
1632 -
1633 - /**
1634 - * Whether the site is currently onboarding or not.
1635 - * A site is considered as being onboarded if it currently has an onboarding token.
1636 - *
1637 - * @since 5.8
1638 - * @deprecated Use \Automattic\Jetpack\Status()->is_onboarding()
1639 - *
1640 - * @access public
1641 - * @static
1642 - *
1643 - * @return bool True if the site is currently onboarding, false otherwise
1644 - */
1645 - public static function is_onboarding() {
1646 - _deprecated_function( __METHOD__, 'jetpack-10.9', 'Automattic\\Jetpack\\Status\\is_onboarding' );
1647 -
1648 - if ( ! method_exists( 'Automattic\Jetpack\Status', 'is_onboarding' ) ) {
1649 - return Jetpack_Options::get_option( 'onboarding' ) !== false;
1650 - }
1651 - return ( new Status() )->is_onboarding();
1652 - }
1653 -
1654 - /**
1655 1836 * Determines reason for Jetpack offline mode.
1656 1837 */
1657 1838 public static function development_mode_trigger_text() {
1658 1839 $status = new Status();
@@ -1666,11 +1847,10 @@
1666 1847 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1667 1848 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1668 1849 } elseif ( $status->is_local_site() ) {
1669 1850 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1670 - /** This filter is documented in packages/status/src/class-status.php */
1671 - } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1672 - $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1851 + } elseif ( get_option( 'jetpack_offline_mode' ) ) {
1852 + $notice = __( 'The jetpack_offline_mode option is set to true.', 'jetpack' );
1673 1853 } else {
1674 1854 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1675 1855 }
1676 1856
@@ -1685,9 +1865,9 @@
1685 1865 if ( ( new Status() )->is_offline_mode() ) {
1686 1866 $notice = sprintf(
1687 1867 /* translators: %s is a URL */
1688 1868 __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1689 - Redirect::get_url( 'jetpack-support-development-mode' )
1869 + esc_url( Redirect::get_url( 'jetpack-support-development-mode' ) )
1690 1870 );
1691 1871
1692 1872 $notice .= ' ' . self::development_mode_trigger_text();
1693 1873
@@ -1696,19 +1876,12 @@
1696 1876
1697 1877 // Throw up a notice if using a development version and as for feedback.
1698 1878 if ( self::is_development_version() ) {
1699 1879 /* translators: %s is a URL */
1700 - $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), Redirect::get_url( 'jetpack-contact-support-beta-group' ) );
1880 + $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), esc_url( Redirect::get_url( 'jetpack-contact-support-beta-group' ) ) );
1701 1881
1702 1882 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1703 1883 }
1704 - // Throw up a notice if using staging mode.
1705 - if ( ( new Status() )->is_staging_site() ) {
1706 - /* translators: %s is a URL */
1707 - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1708 -
1709 - echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All provided text.
1710 - }
1711 1884 }
1712 1885
1713 1886 /**
1714 1887 * Whether Jetpack's version maps to a public release, or a development version.
@@ -1729,28 +1902,8 @@
1729 1902 );
1730 1903 }
1731 1904
1732 1905 /**
1733 - * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1734 - *
1735 - * @param int $user_id User ID or will use get_current_user_id if false/not provided.
1736 - */
1737 - public static function is_user_connected( $user_id = false ) {
1738 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1739 - return self::connection()->is_user_connected( $user_id );
1740 - }
1741 -
1742 - /**
1743 - * Get the wpcom user data of the current|specified connected user.
1744 - *
1745 - * @param null|int $user_id User ID or will use get_current_user_id if null.
1746 - */
1747 - public static function get_connected_user_data( $user_id = null ) {
1748 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1749 - return self::connection()->get_connected_user_data( $user_id );
1750 - }
1751 -
1752 - /**
1753 1906 * Get the wpcom email of the current|specified connected user.
1754 1907 *
1755 1908 * @param null|int $user_id User ID or will use get_current_user_id if null.
1756 1909 */
@@ -1802,18 +1955,11 @@
1802 1955 */
1803 1956 public static function load_modules() {
1804 1957 $status = new Status();
1805 1958
1806 - if ( method_exists( $status, 'is_onboarding' ) ) {
1807 - $is_onboarding = $status->is_onboarding();
1808 - } else {
1809 - $is_onboarding = self::is_onboarding();
1810 - }
1811 -
1812 1959 if (
1813 1960 ! self::is_connection_ready()
1814 1961 && ! $status->is_offline_mode()
1815 - && ! $is_onboarding
1816 1962 && (
1817 1963 ! is_multisite()
1818 1964 || ! get_site_option( 'jetpack_protect_active' )
1819 1965 )
@@ -1934,22 +2080,14 @@
1934 2080 *
1935 2081 * @todo Store the result in core's object cache maybe?
1936 2082 */
1937 2083 public static function get_active_plugins() {
1938 - $active_plugins = (array) get_option( 'active_plugins', array() );
1939 -
1940 - if ( is_multisite() ) {
1941 - // Due to legacy code, active_sitewide_plugins stores them in the keys,
1942 - // whereas active_plugins stores them in the values.
1943 - $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1944 - if ( $network_plugins ) {
1945 - $active_plugins = array_merge( $active_plugins, $network_plugins );
1946 - }
2084 + // Older Connection copies can load first and lack this method.
2085 + if ( ! method_exists( Heartbeat::class, 'get_active_plugins' ) ) {
2086 + return array();
1947 2087 }
1948 2088
1949 - sort( $active_plugins );
1950 -
1951 - return array_unique( $active_plugins );
2089 + return Heartbeat::get_active_plugins();
1952 2090 }
1953 2091
1954 2092 /**
1955 2093 * Gets and parses additional plugin data to send with the heartbeat data
@@ -2087,8 +2225,10 @@
2087 2225 *
2088 2226 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2089 2227 */
2090 2228 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2229 + // @todo Remove this require once the deprecated Jetpack_Twitter_Cards wrapper has been removed.
2230 + // Twitter Cards functionality now lives in the jetpack-post-media package (Automattic\Jetpack\Post_Media\Twitter_Cards).
2091 2231 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2092 2232 }
2093 2233 }
2094 2234
@@ -2191,9 +2331,9 @@
2191 2331 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2192 2332 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're not changing the site.
2193 2333 }
2194 2334 wp_safe_redirect( self::admin_url( 'page=' . rawurlencode( $page ) ) );
2195 - exit;
2335 + exit( 0 );
2196 2336 }
2197 2337 }
2198 2338
2199 2339 /**
@@ -2240,8 +2380,12 @@
2240 2380 default:
2241 2381 break;
2242 2382 }
2243 2383 }
2384 + if ( method_exists( Feature_Policy::class, 'ensure_hooks' ) ) {
2385 + Feature_Policy::ensure_hooks();
2386 + }
2387 +
2244 2388 /**
2245 2389 * Filters the array of default modules.
2246 2390 *
2247 2391 * @since 2.5.0
@@ -2273,12 +2417,14 @@
2273 2417 * @return array
2274 2418 */
2275 2419 public function handle_deprecated_modules( $modules ) {
2276 2420 $deprecated_modules = array(
2277 - 'debug' => null, // Closed out and moved to the debugger library.
2278 - 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2279 - 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2280 - 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2421 + 'debug' => null, // Closed out and moved to the debugger library.
2422 + 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2423 + 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2424 + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2425 + 'lazy-images' => null, // Closed out in 12.8 -- WordPress core now has native lazy loading.
2426 + 'enhanced-distribution' => null, // Closed out in 13.3 -- WP.com is winding down the firehose.
2281 2427 );
2282 2428
2283 2429 // Don't activate SSO if they never completed activating WPCC.
2284 2430 if ( self::is_module_active( 'wpcc' ) ) {
@@ -2332,8 +2478,17 @@
2332 2478 }
2333 2479 }
2334 2480 }
2335 2481
2482 + // Special case to convert block setting to a block module.
2483 + $block_key = array_search( 'blocks', $modules, true );
2484 + if ( $block_key !== false ) { // Only care if 'blocks' made it through the previous filters.
2485 + $block_option = get_option( 'jetpack_blocks_disabled', null );
2486 + if ( $block_option ) {
2487 + unset( $modules[ $block_key ] );
2488 + }
2489 + }
2490 +
2336 2491 return $modules;
2337 2492 }
2338 2493
2339 2494 /**
@@ -2390,24 +2545,34 @@
2390 2545
2391 2546 /**
2392 2547 * Return module name translation. Uses matching string created in modules/module-headings.php.
2393 2548 *
2549 + * The module list is globbed from `modules/` at runtime, so a module can be listed with no
2550 + * entry in that generated file. Fall back to the untranslated header rather than overwriting
2551 + * it with the null `jetpack_get_module_i18n()` returns for an unknown slug.
2552 + *
2394 2553 * @since 3.9.2
2395 2554 *
2396 2555 * @param array $modules Array of Jetpack modules.
2397 2556 *
2398 - * @return string|void
2557 + * @return array
2399 2558 */
2400 2559 public static function get_translated_modules( $modules ) {
2401 2560 foreach ( $modules as $index => $module ) {
2402 2561 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2403 - if ( isset( $module['name'] ) ) {
2404 - $modules[ $index ]['name'] = $i18n_module['name'];
2562 + $name = $i18n_module['name'] ?? null;
2563 + $description = $i18n_module['description'] ?? null;
2564 +
2565 + if ( null !== $name && isset( $module['name'] ) ) {
2566 + $modules[ $index ]['name'] = $name;
2405 2567 }
2406 - if ( isset( $module['description'] ) ) {
2407 - $modules[ $index ]['description'] = $i18n_module['description'];
2408 - $modules[ $index ]['short_description'] = $i18n_module['description'];
2568 + if ( null !== $description && isset( $module['description'] ) ) {
2569 + $modules[ $index ]['description'] = $description;
2570 + $modules[ $index ]['short_description'] = $description;
2409 2571 }
2572 + if ( isset( $module['module_tags'] ) ) {
2573 + $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2574 + }
2410 2575 }
2411 2576 return $modules;
2412 2577 }
2413 2578
@@ -2443,20 +2608,28 @@
2443 2608
2444 2609 /**
2445 2610 * Catches PHP errors. Must be used in conjunction with output buffering.
2446 2611 *
2612 + * @deprecated since 13.5
2447 2613 * @param bool $catch True to start catching, False to stop.
2448 2614 *
2449 2615 * @static
2616 + * @deprecated 13.5
2617 + * @see \Automattic\Jetpack\Errors
2450 2618 */
2451 2619 public static function catch_errors( $catch ) {
2620 + _deprecated_function( __METHOD__, '13.5' );
2621 + // @phan-suppress-next-line PhanDeprecatedClass
2452 2622 return ( new Errors() )->catch_errors( $catch );
2453 2623 }
2454 2624
2455 2625 /**
2456 2626 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2627 + *
2628 + * @deprecated since 13.5
2457 2629 */
2458 2630 public static function catch_errors_on_shutdown() {
2631 + _deprecated_function( __METHOD__, '13.5' );
2459 2632 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2460 2633 }
2461 2634
2462 2635 /**
@@ -2548,9 +2721,9 @@
2548 2721 }
2549 2722
2550 2723 if ( $deactivated ) {
2551 2724 if ( $send_state_messages ) {
2552 - self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2725 + self::state( 'deactivated_plugins', implode( ',', $deactivated ) );
2553 2726 }
2554 2727
2555 2728 if ( $redirect ) {
2556 2729 $url = add_query_arg(
@@ -2560,9 +2733,9 @@
2560 2733 ),
2561 2734 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2562 2735 );
2563 2736 wp_safe_redirect( $url );
2564 - exit;
2737 + exit( 0 );
2565 2738 }
2566 2739 }
2567 2740
2568 2741 /**
@@ -2580,9 +2753,8 @@
2580 2753
2581 2754 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating.
2582 2755 if ( $send_state_messages ) {
2583 2756 self::restate();
2584 - self::catch_errors( true );
2585 2757 }
2586 2758
2587 2759 $active = self::get_active_modules();
2588 2760
@@ -2650,10 +2822,8 @@
2650 2822 if ( $send_state_messages ) {
2651 2823 self::state( 'error', false );
2652 2824 self::state( 'module', false );
2653 2825 }
2654 -
2655 - self::catch_errors( false );
2656 2826 /**
2657 2827 * Fires when default modules are activated.
2658 2828 *
2659 2829 * @since 1.9.0
@@ -2711,9 +2881,9 @@
2711 2881 * @return string $url module configuration URL.
2712 2882 */
2713 2883 public static function module_configuration_url( $module ) {
2714 2884 $module = self::get_module_slug( $module );
2715 - $default_url = self::admin_url() . "#/settings?term=$module";
2885 + $default_url = self::admin_url( array( 'page' => 'jetpack-settings' ) ) . "#/settings?term=$module";
2716 2886 /**
2717 2887 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
2718 2888 *
2719 2889 * @since 6.9.0
@@ -2731,9 +2901,9 @@
2731 2901 *
2732 2902 * @param string $message Error message.
2733 2903 * @param bool $deactivate Deactivate Jetpack or not.
2734 2904 *
2735 - * @return void
2905 + * @return never
2736 2906 */
2737 2907 public static function bail_on_activation( $message, $deactivate = true ) {
2738 2908 ?>
2739 2909 <!doctype html>
@@ -2771,9 +2941,9 @@
2771 2941 if ( $update ) {
2772 2942 update_option( 'active_plugins', array_filter( $plugins ) );
2773 2943 }
2774 2944 }
2775 - exit;
2945 + exit( 0 );
2776 2946 }
2777 2947
2778 2948 /**
2779 2949 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
@@ -2798,12 +2968,18 @@
2798 2968 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
2799 2969
2800 2970 Health::on_jetpack_activated();
2801 2971
2972 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
2973 +
2802 2974 if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) {
2803 2975 Sync_Actions::do_only_first_initial_sync();
2804 2976 }
2805 2977
2978 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
2979 + Woocommerce_Analytics::maybe_add_proxy_speed_module();
2980 + }
2981 +
2806 2982 self::plugin_initialize();
2807 2983 }
2808 2984
2809 2985 /**
@@ -2838,9 +3014,9 @@
2838 3014
2839 3015 if ( $plugins_path === $referer['path'] ) {
2840 3016 $source_type = 'list';
2841 3017 } elseif ( $plugins_install_path === $referer['path'] ) {
2842 - $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3018 + $tab = $query_parts['tab'] ?? 'featured';
2843 3019 switch ( $tab ) {
2844 3020 case 'popular':
2845 3021 $source_type = 'popular';
2846 3022 break;
@@ -2850,10 +3026,10 @@
2850 3026 case 'favorites':
2851 3027 $source_type = 'favorites';
2852 3028 break;
2853 3029 case 'search':
2854 - $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
2855 - $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3030 + $source_type = 'search-' . ( $query_parts['type'] ?? 'term' );
3031 + $source_query = $query_parts['s'] ?? null;
2856 3032 break;
2857 3033 default:
2858 3034 $source_type = 'featured';
2859 3035 }
@@ -2862,29 +3038,171 @@
2862 3038 return array( $source_type, $source_query );
2863 3039 }
2864 3040
2865 3041 /**
2866 - * Runs before bumping version numbers up to a new version
3042 + * Runs before bumping version numbers up to a new version.
2867 3043 *
2868 - * @param string $version Version:timestamp.
3044 + * Only ever registered the hooks for the release post update modal, which has been removed.
3045 + * No longer hooked to `updating_jetpack_version`.
3046 + *
3047 + * @deprecated 16.2
3048 + *
3049 + * @param string $version Version:timestamp.
2869 3050 * @param string $old_version Old Version:timestamp or false if not set yet.
2870 3051 */
2871 - public static function do_version_bump( $version, $old_version ) {
2872 - if ( $old_version ) { // For existing Jetpack installations.
2873 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3052 + public static function do_version_bump( $version, $old_version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Signature preserved for the deprecation shim.
3053 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
3054 + }
2874 3055
2875 - // If a front end page is visited after the update, the 'wp' action will fire.
2876 - add_action( 'wp', 'Jetpack::set_update_modal_display' );
3056 + /**
3057 + * Enables the Newsletter (subscriptions) module for existing sites now that it is a default-on module.
3058 + *
3059 + * Fresh installs receive the module via its "Auto Activate: Yes" header, so this only handles sites
3060 + * upgrading from a version where the module defaulted off. It runs once per site (guarded by the
3061 + * subscriptions_default_on_migrated option). Fresh installs are marked as migrated immediately so the
3062 + * migration never runs for them. After it has run, the user's choice to deactivate the module again
3063 + * (for example from the My Jetpack Products page) is respected and never reverted.
3064 + *
3065 + * The module requires a connection, so on a disconnected site the migration is deferred without setting
3066 + * the guard, allowing a later version bump to retry once the site is connected.
3067 + *
3068 + * @param string $version New Jetpack version:timestamp.
3069 + * @param string|false $old_version Previous Jetpack version:timestamp, or false on a fresh install.
3070 + */
3071 + public static function activate_subscriptions_module_for_existing_sites( $version, $old_version ) {
3072 + if ( get_option( 'jetpack_subscriptions_default_on_migrated' ) ) {
3073 + return;
3074 + }
2877 3075
2878 - // If an admin page is visited after the update, the 'current_screen' action will fire.
2879 - add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3076 + // Fresh installs get the module via its "Auto Activate: Yes" header. Mark them as migrated so a
3077 + // later opt-out is never reverted by the existing-site path on a subsequent version bump.
3078 + if ( ! $old_version ) {
3079 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3080 + return;
2880 3081 }
3082 +
3083 + if ( ! self::is_connection_ready() ) {
3084 + return;
3085 + }
3086 +
3087 + // Mark as migrated only once the module is active, so a transient activation failure is retried on
3088 + // a later version bump rather than being silently skipped.
3089 + if ( self::is_module_active( 'subscriptions' ) || self::activate_module( 'subscriptions', false, false ) ) {
3090 + update_option( 'jetpack_subscriptions_default_on_migrated', true );
3091 + }
2881 3092 }
2882 3093
2883 3094 /**
3095 + * Option flag that records the AI master-switch opt-out reconciliation has run,
3096 + * so it never runs twice.
3097 + *
3098 + * @var string
3099 + */
3100 + const AI_MASTER_OPTOUT_MIGRATED_OPTION = 'jetpack_ai_master_optout_migrated';
3101 +
3102 + /**
3103 + * Register the on-upgrade init hooks whose relative ORDER matters, extracted so
3104 + * the ordering can be asserted in tests without invoking plugin_upgrade() (whose
3105 + * guards make it unreliable to trigger under test). activate_new_modules()
3106 + * (init, default priority 10) auto-activates "Auto Activate: Yes" modules
3107 + * including the `ai` master; reconcile_ai_master_optout() must run at a LATER
3108 + * priority to honor an explicit AI opt-out.
3109 + *
3110 + * @return void
3111 + */
3112 + public static function register_upgrade_init_hooks() {
3113 + add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
3114 + add_action( 'init', array( __CLASS__, 'reconcile_ai_master_optout' ), 20 );
3115 + }
3116 +
3117 + /**
3118 + * Preserves an explicit Jetpack AI opt-out when the `ai` module becomes the site-wide
3119 + * master switch off WordPress.com Simple (self-hosted and Atomic).
3120 + *
3121 + * The `ai` module is "Auto Activate: Yes", so on upgrade {@see self::activate_new_modules()}
3122 + * turns it on for connected sites — the desired default-on / auto-enable-on-connection
3123 + * behavior, which this method deliberately leaves alone. The one case it corrects is a site
3124 + * that had explicitly disabled Jetpack AI (the `jetpack_ai_enabled` option present and falsey)
3125 + * before the module shipped: that opt-out must survive the module becoming the master, so the
3126 + * module is deactivated for exactly those sites. An absent or truthy option is left untouched.
3127 + *
3128 + * Ordering is the whole point. `activate_new_modules()` is hooked on `init` at priority 10 and
3129 + * auto-activates the module there; this method is hooked on `init` at priority 20 (see
3130 + * {@see self::plugin_upgrade()}), so it runs AFTER the auto-activation and its deactivation is
3131 + * the final state. A version-guarded block that ran inline during `plugins_loaded` would be
3132 + * undone by the later auto-activation, which is why this is a late-init hook rather than an
3133 + * inline upgrade step.
3134 + *
3135 + * WordPress.com Simple never runs modules — the option stays the master there — so this is a
3136 + * no-op on Simple. The {@see self::AI_MASTER_OPTOUT_MIGRATED_OPTION} flag makes it run exactly
3137 + * once, which matters because off-Simple the option is no longer the master after this runs:
3138 + * a stale falsey option must not keep re-deactivating a module the user later turns back on.
3139 + *
3140 + * @return void
3141 + */
3142 + public static function reconcile_ai_master_optout() {
3143 + if ( get_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION ) ) {
3144 + return;
3145 + }
3146 +
3147 + // Simple keeps the `jetpack_ai_enabled` option as the master; modules don't run there.
3148 + if ( ( new Host() )->is_wpcom_simple() ) {
3149 + return;
3150 + }
3151 +
3152 + // A sentinel default distinguishes an absent option (leave auto-activation alone) from one
3153 + // explicitly stored falsey (an opt-out to preserve).
3154 + $stored = get_option( 'jetpack_ai_enabled', 'not-set' );
3155 + if ( 'not-set' !== $stored && ! (bool) $stored ) {
3156 + ( new Modules() )->deactivate( 'ai' );
3157 + }
3158 +
3159 + update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3160 + }
3161 +
3162 + /**
3163 + * Deletes obsolete SEO module-state options without changing module activation.
3164 + *
3165 + * @since 16.3
3166 + */
3167 + public static function cleanup_seo_module_state_options() {
3168 + delete_option( 'jetpack_seo_sitemap_enabled' );
3169 + delete_option( 'jetpack_seo_canonical_urls_enabled' );
3170 + delete_option( 'jetpack_seo_module_state_reconciled' );
3171 + }
3172 +
3173 + /**
3174 + * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3175 + * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3176 + *
3177 + * Hooked on `updating_jetpack_version`, which fires on every install including the
3178 + * first — with `$old_version === false` on a brand-new site (the same signal
3179 + * {@see self::activate_subscriptions_module_for_existing_sites()} keys off). Fresh
3180 + * installs are seeded visible; existing installs are seeded hidden and opt in later
3181 + * via the legacy Traffic page or My Jetpack. `add_option()` makes this seed-once: it
3182 + * never overrides a value a later opt-in (or opt-out) has set. WordPress.com sites
3183 + * ignore this option entirely (always visible) — see
3184 + * {@see \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()}.
3185 + *
3186 + * @param string $version The new Jetpack version (unused).
3187 + * @param string|false $old_version The previous version, or false on a fresh install.
3188 + */
3189 + public static function seed_seo_visibility_cohort( $version, $old_version ) {
3190 + add_option( Jetpack_SEO_Initializer::VISIBILITY_OPTION, ! $old_version );
3191 + }
3192 +
3193 + /**
2884 3194 * Sets the display_update_modal state.
3195 + *
3196 + * The release post update modal that read this state has been removed. The write is kept so the
3197 + * method still behaves as documented for the deprecation window. When this is deleted, also drop
3198 + * the matching `display_update_modal` guard in Automattic\Jetpack\CookieState::should_set_cookie(),
3199 + * which exists only to keep this key out of the cookie on the Jetpack admin screen.
3200 + *
3201 + * @deprecated 16.2
2885 3202 */
2886 3203 public static function set_update_modal_display() {
3204 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2887 3205 self::state( 'display_update_modal', true );
2888 3206 }
2889 3207
2890 3208 /**
@@ -2889,11 +3207,16 @@
2889 3207
2890 3208 /**
2891 3209 * Enqueues the block library styles.
2892 3210 *
3211 + * Only ever used by the release post update modal, which has been removed.
3212 + *
3213 + * @deprecated 16.2
3214 + *
2893 3215 * @param string $hook The current admin page.
2894 3216 */
2895 3217 public static function enqueue_block_style( $hook ) {
3218 + _deprecated_function( __METHOD__, 'jetpack-16.2' );
2896 3219 if ( 'toplevel_page_jetpack' === $hook ) {
2897 3220 wp_enqueue_style( 'wp-block-library' );
2898 3221 }
2899 3222 }
@@ -2922,9 +3245,8 @@
2922 3245
2923 3246 self::load_modules();
2924 3247
2925 3248 Jetpack_Options::delete_option( 'do_activate' );
2926 - Jetpack_Options::delete_option( 'dismissed_connection_banner' );
2927 3249 }
2928 3250
2929 3251 /**
2930 3252 * Handles the activation of the default modules depending on the current state of the site:
@@ -2983,8 +3305,12 @@
2983 3305 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
2984 3306 self::disconnect();
2985 3307 Jetpack_Options::delete_option( 'version' );
2986 3308 }
3309 +
3310 + if ( ! defined( 'WC_ANALYTICS' ) && class_exists( 'Automattic\Woocommerce_Analytics' ) ) {
3311 + Woocommerce_Analytics::maybe_remove_proxy_speed_module();
3312 + }
2987 3313 }
2988 3314
2989 3315 /**
2990 3316 * Set activated option to 4 on jetpack_idc_disconnect action.
@@ -3012,9 +3338,9 @@
3012 3338 $connection->remove_connection( ! Identity_Crisis::validate_sync_error_idc_option() );
3013 3339 }
3014 3340
3015 3341 /**
3016 - * Happens after a successfull disconnection.
3342 + * Happens after a successful disconnection.
3017 3343 *
3018 3344 * @static
3019 3345 */
3020 3346 public static function jetpack_site_disconnected() {
@@ -3019,8 +3345,10 @@
3019 3345 */
3020 3346 public static function jetpack_site_disconnected() {
3021 3347 Identity_Crisis::clear_all_idc_options();
3022 3348
3349 + \Automattic\Jetpack\Reprint_Export\Reprint_Exporter::discard_credentials();
3350 +
3023 3351 // Delete all the sync related data. Since it could be taking up space.
3024 3352 Sender::get_instance()->uninstall();
3025 3353
3026 3354 /**
@@ -3037,10 +3365,13 @@
3037 3365 }
3038 3366 }
3039 3367
3040 3368 /**
3041 - * Disconnects the user
3369 + * Disconnects the user.
3042 3370 *
3371 + * @deprecated 13.4
3372 + * @see \Automattic\Jetpack\Connection\Manager::disconnect_user()
3373 + *
3043 3374 * @param int $user_id The user ID to disconnect.
3044 3375 */
3045 3376 public function disconnect_user( $user_id ) {
3046 3377 $this->connection_manager->disconnect_user( $user_id );
@@ -3046,21 +3377,8 @@
3046 3377 $this->connection_manager->disconnect_user( $user_id );
3047 3378 }
3048 3379
3049 3380 /**
3050 - * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3051 - *
3052 - * @deprecated since Jetpack 9.7.0
3053 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
3054 - *
3055 - * @return bool|WP_Error
3056 - */
3057 - public static function try_registration() {
3058 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3059 - return static::connection()->try_registration();
3060 - }
3061 -
3062 - /**
3063 3381 * Checking the domain names in beta versions.
3064 3382 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3065 3383 *
3066 3384 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
@@ -3067,9 +3385,9 @@
3067 3385 *
3068 3386 * @return null|\WP_Error The domain validation error, or `null` if everything's fine.
3069 3387 */
3070 3388 public static function registration_check_domains( $error ) {
3071 - if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3389 + if ( static::is_development_version() ) {
3072 3390 $domains_to_check = array_unique(
3073 3391 array(
3074 3392 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3075 3393 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
@@ -3094,10 +3412,17 @@
3094 3412 * @param mixed $code Error code to log.
3095 3413 * @param mixed $data Data to log.
3096 3414 */
3097 3415 public static function log( $code, $data = null ) {
3416 +
3417 + $raw_log = Jetpack_Options::get_option( 'log', array() );
3418 + // This can be modified by the `jetpack_options` filter, so abort if we don't have an array.
3419 + if ( ! is_array( $raw_log ) ) {
3420 + return;
3421 + }
3422 +
3098 3423 // only grab the latest 200 entries.
3099 - $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3424 + $log = array_slice( $raw_log, -199, 199 );
3100 3425
3101 3426 // Append our event to the log.
3102 3427 $log_entry = array(
3103 3428 'time' => time(),
@@ -3197,8 +3522,15 @@
3197 3522
3198 3523 /**
3199 3524 * Return stat data for WPCOM sync.
3200 3525 *
3526 + * The Sync stats module was this method's last caller and moved to the Connection package's
3527 + * `Heartbeat::generate_stats_array()`, which assembles the heartbeat data through the
3528 + * `jetpack_heartbeat_stats_array` filter. Note the package method does not include the extended
3529 + * data from `get_additional_stat_data()`, so callers relying on `$extended` need to add it themselves.
3530 + *
3531 + * @deprecated 16.2
3532 + *
3201 3533 * @param bool $encode JSON encode the result.
3202 3534 * @param bool $extended Adds additional stats data.
3203 3535 *
3204 3536 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
@@ -3203,10 +3535,15 @@
3203 3535 *
3204 3536 * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true.
3205 3537 */
3206 3538 public static function get_stat_data( $encode = true, $extended = true ) {
3207 - $data = Jetpack_Heartbeat::generate_stats_array();
3539 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3208 3540
3541 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
3542 + ? Heartbeat::get_environment_stats()
3543 + : array();
3544 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), $env_stats );
3545 +
3209 3546 if ( $extended ) {
3210 3547 $additional_data = self::get_additional_stat_data();
3211 3548 $data = array_merge( $data, $additional_data );
3212 3549 }
@@ -3211,9 +3548,9 @@
3211 3548 $data = array_merge( $data, $additional_data );
3212 3549 }
3213 3550
3214 3551 if ( $encode ) {
3215 - return wp_json_encode( $data );
3552 + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
3216 3553 }
3217 3554
3218 3555 return $data;
3219 3556 }
@@ -3285,38 +3622,34 @@
3285 3622 $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
3286 3623 /** Already documented in automattic/jetpack-connection::src/class-client.php */
3287 3624 $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false );
3288 3625
3289 - if ( ! $is_offline_mode ) {
3290 - Jetpack_Connection_Banner::init();
3291 - Jetpack_Connection_Widget::init();
3292 - }
3626 + // Run post-activation actions if needed.
3627 + $this->plugin_post_activation();
3293 3628
3294 3629 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3295 3630 // Upgrade: 1.1 -> 1.1.1
3296 3631 // Check and see if host can verify the Jetpack servers' SSL certificate.
3297 3632 $args = array();
3633 + // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
3298 3634 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3299 3635 }
3300 3636
3301 - Jetpack_Recommendations_Banner::init();
3302 -
3303 - if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3637 + if (
3638 + current_user_can( 'manage_options' )
3639 + && ! self::permit_ssl()
3640 + && ! $is_offline_mode
3641 + ) {
3304 3642 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3305 3643 }
3306 3644
3307 3645 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3308 3646 add_action( 'load-plugins.php', array( $this, 'plugins_page_init_jetpack_state' ) );
3309 - add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3310 3647
3311 3648 if ( ! ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) && ! is_network_admin() ) ) {
3312 3649 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3313 3650 }
3314 3651
3315 - if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3316 - add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3317 - }
3318 -
3319 3652 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3320 3653
3321 3654 if ( self::is_connection_ready() || $is_offline_mode ) {
3322 3655 // Artificially throw errors in certain specific cases during plugin activation.
@@ -3321,13 +3654,8 @@
3321 3654 if ( self::is_connection_ready() || $is_offline_mode ) {
3322 3655 // Artificially throw errors in certain specific cases during plugin activation.
3323 3656 add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3324 3657 }
3325 -
3326 - // Add custom column in wp-admin/users.php to show whether user is linked.
3327 - add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3328 - add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3329 - add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3330 3658 }
3331 3659
3332 3660 /**
3333 3661 * Adds body classes.
@@ -3360,8 +3688,9 @@
3360 3688 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3361 3689 * This function artificially throws errors for such cases (per a specific list).
3362 3690 *
3363 3691 * @param string $plugin The activated plugin.
3692 + * @throws RuntimeException If a conflicting plugin is detected.
3364 3693 */
3365 3694 public function throw_error_on_activate_plugin( $plugin ) {
3366 3695 $active_modules = self::get_active_modules();
3367 3696
@@ -3374,8 +3703,9 @@
3374 3703 if ( 'stats.php' === basename( $plugin ) ) {
3375 3704 $throw = true;
3376 3705 }
3377 3706 } else {
3707 + // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Checked above. See also https://github.com/phan/phan/issues/1204.
3378 3708 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3379 3709 if ( basename( $plugin ) === basename( $reflection->getFileName() ) ) {
3380 3710 $throw = true;
3381 3711 }
@@ -3382,9 +3712,9 @@
3382 3712 }
3383 3713
3384 3714 if ( $throw ) {
3385 3715 /* translators: Plugin name to deactivate. */
3386 - trigger_error( sprintf( esc_html__( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
3716 + throw new RuntimeException( sprintf( __( 'Jetpack contains the most recent version of the old "%1$s" plugin.', 'jetpack' ), 'WordPress.com Stats' ) );
3387 3717 }
3388 3718 }
3389 3719 }
3390 3720
@@ -3466,8 +3796,9 @@
3466 3796 /**
3467 3797 * Handler for Jetpack remote file uploads.
3468 3798 *
3469 3799 * @access public
3800 + * @return never
3470 3801 */
3471 3802 public function remote_request_handlers() {
3472 3803 switch ( current_filter() ) {
3473 3804 case 'wp_ajax_nopriv_jetpack_upload_file':
@@ -3494,18 +3825,17 @@
3494 3825 if ( ! is_int( $status_code ) ) {
3495 3826 $status_code = 400;
3496 3827 }
3497 3828
3498 - status_header( $status_code );
3499 - die( wp_json_encode( (object) compact( 'error', 'error_description' ) ) );
3829 + wp_send_json( (object) compact( 'error', 'error_description' ), $status_code, JSON_UNESCAPED_SLASHES );
3500 3830 }
3501 3831
3502 - status_header( 200 );
3503 3832 if ( true === $response ) {
3504 - exit;
3833 + status_header( 200 );
3834 + exit( 0 );
3505 3835 }
3506 3836
3507 - die( wp_json_encode( (object) $response ) );
3837 + wp_send_json( (object) $response, 200, JSON_UNESCAPED_SLASHES );
3508 3838 }
3509 3839
3510 3840 /**
3511 3841 * Uploads a file gotten from the global $_FILES.
@@ -3513,9 +3843,9 @@
3513 3843 * the attachment file of the media item (gotten through of the post_id)
3514 3844 * will be updated instead of add a new one.
3515 3845 *
3516 3846 * @param boolean $update_media_item - update media attachment.
3517 - * @return array - An array describing the uploadind files process.
3847 + * @return array|WP_Error - An array describing the uploading files process.
3518 3848 */
3519 3849 public function upload_handler( $update_media_item = false ) {
3520 3850 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
3521 3851 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
@@ -3566,9 +3896,9 @@
3566 3896 return new WP_Error( 'handler_cannot_upload', __( 'The upload handler cannot upload files', 'jetpack' ), 400 );
3567 3897 }
3568 3898
3569 3899 $uploaded_files = array();
3570 - $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3900 + $global_post = $GLOBALS['post'] ?? null;
3571 3901 unset( $GLOBALS['post'] );
3572 3902 if ( empty( $_FILES['media']['name'] ) ) {
3573 3903 // Nothing to process, just return.
3574 3904 return $uploaded_files;
@@ -3575,9 +3905,9 @@
3575 3905 }
3576 3906 foreach ( $_FILES['media']['name'] as $index => $name ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, unslash sniff is wrong. Validation should happen below.
3577 3907 $file = array();
3578 3908 foreach ( $media_keys as $media_key ) {
3579 - $file[ $media_key ] = isset( $_FILES['media'][ $media_key ][ $index ] ) ? $_FILES['media'][ $media_key ][ $index ] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3909 + $file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ] ?? null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,,WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- As above, the unslash sniff is wrong.
3580 3910 }
3581 3911
3582 3912 list( $hmac_provided, $salt ) = isset( $_POST['_jetpack_file_hmac_media'][ $index ] ) ? explode( ':', filter_var( wp_unslash( $_POST['_jetpack_file_hmac_media'][ $index ] ) ) ) : array( 'no', '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce should have been checked by the caller.
3583 3913
@@ -3623,9 +3953,9 @@
3623 3953 'type' => (string) $edited_media_item->post_mime_type,
3624 3954 'meta' => (array) wp_get_attachment_metadata( $post_id ),
3625 3955 );
3626 3956
3627 - return (array) array( $response );
3957 + return array( $response );
3628 3958 }
3629 3959
3630 3960 $attachment_id = media_handle_upload(
3631 3961 '.jetpack.upload.',
@@ -3664,94 +3994,8 @@
3664 3994 return $uploaded_files;
3665 3995 }
3666 3996
3667 3997 /**
3668 - * Add help to the Jetpack page
3669 - *
3670 - * @since Jetpack (1.2.3)
3671 - * @return void
3672 - */
3673 - public function admin_help() {
3674 - $current_screen = get_current_screen();
3675 -
3676 - // Overview.
3677 - $current_screen->add_help_tab(
3678 - array(
3679 - 'id' => 'home',
3680 - 'title' => __( 'Home', 'jetpack' ),
3681 - 'content' =>
3682 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3683 - '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3684 - '<p>' . __( 'On this page, you are able to view the modules available within Jetpack, learn more about them, and activate or deactivate them as needed.', 'jetpack' ) . '</p>',
3685 - )
3686 - );
3687 -
3688 - // Screen Content.
3689 - if ( current_user_can( 'manage_options' ) ) {
3690 - $current_screen->add_help_tab(
3691 - array(
3692 - 'id' => 'settings',
3693 - 'title' => __( 'Settings', 'jetpack' ),
3694 - 'content' =>
3695 - '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
3696 - '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3697 - '<ol>' .
3698 - '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3699 - '<li>' . __( 'Using the checkboxes next to each module, you can select multiple modules to toggle via the Bulk Actions menu at the top of the list.', 'jetpack' ) . '</li>' .
3700 - '</ol>' .
3701 - '<p>' . __( 'Using the tools on the right, you can search for specific modules, filter by module categories or which are active, or change the sorting order.', 'jetpack' ) . '</p>',
3702 - )
3703 - );
3704 - }
3705 -
3706 - // Help Sidebar.
3707 - $support_url = Redirect::get_url( 'jetpack-support' );
3708 - $faq_url = Redirect::get_url( 'jetpack-faq' );
3709 - $current_screen->set_help_sidebar(
3710 - '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3711 - '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3712 - '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3713 - '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3714 - );
3715 - }
3716 -
3717 - /**
3718 - * Enqueues the jetpack-icons style.
3719 - *
3720 - * @return void
3721 - */
3722 - public function admin_menu_css() {
3723 - wp_enqueue_style( 'jetpack-icons' );
3724 - }
3725 -
3726 - /**
3727 - * Registers/enqueues Jetpack banner styles.
3728 - *
3729 - * @return void
3730 - */
3731 - public function admin_banner_styles() {
3732 - $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3733 -
3734 - if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3735 - wp_register_style(
3736 - 'jetpack-dops-style',
3737 - plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3738 - array(), // Load styles for components so the modal can be used.
3739 - JETPACK__VERSION
3740 - );
3741 - }
3742 -
3743 - wp_enqueue_style(
3744 - 'jetpack',
3745 - plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3746 - array( 'jetpack-dops-style' ),
3747 - JETPACK__VERSION . '-20121016'
3748 - );
3749 - wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3750 - wp_style_add_data( 'jetpack', 'suffix', $min );
3751 - }
3752 -
3753 - /**
3754 3998 * Add action links for the Jetpack plugin.
3755 3999 *
3756 4000 * @param array $actions Plugin actions.
3757 4001 *
@@ -3757,14 +4001,11 @@
3757 4001 *
3758 4002 * @return array
3759 4003 */
3760 4004 public function plugin_action_links( $actions ) {
3761 - $support_link = ( new Host() )->is_woa_site() ? 'https://wordpress.com/help/contact/' : self::admin_url( 'page=jetpack-debugger' );
3762 -
3763 4005 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
3764 4006 return array_merge(
3765 - array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3766 - array( 'support' => sprintf( '<a href="%s">%s</a>', $support_link, __( 'Support', 'jetpack' ) ) ),
4007 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack-settings#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
3767 4008 $actions
3768 4009 );
3769 4010 }
3770 4011
@@ -3792,14 +4033,10 @@
3792 4033 'jetpack-plugins-page-js',
3793 4034 '_inc/build/plugins-page.js',
3794 4035 JETPACK__PLUGIN_FILE,
3795 4036 array(
3796 - 'in_footer' => true,
3797 - 'textdomain' => 'jetpack',
3798 - 'dependencies' => array(
3799 - 'wp-polyfill',
3800 - 'wp-components',
3801 - ),
4037 + 'in_footer' => true,
4038 + 'textdomain' => 'jetpack',
3802 4039 )
3803 4040 );
3804 4041 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3805 4042
@@ -3804,9 +4041,9 @@
3804 4041 Assets::enqueue_script( 'jetpack-plugins-page-js' );
3805 4042
3806 4043 // Add objects to be passed to the initial state of the app.
3807 4044 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
3808 - wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state() ) ) . '"));', 'before' );
4045 + wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_plugins_page_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
3809 4046
3810 4047 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
3811 4048 }
3812 4049 }
@@ -3811,9 +4048,9 @@
3811 4048 }
3812 4049 }
3813 4050
3814 4051 /**
3815 - * Outputs the wrapper for the plugin deactivation modal
4052 + * Outputs the wrapper for the plugin modal
3816 4053 * Contents are loaded by React script
3817 4054 *
3818 4055 * @return void
3819 4056 */
@@ -3855,9 +4092,9 @@
3855 4092 // @todo provide way to go to specific calypso env.
3856 4093 self::get_calypso_host() . 'jetpack/connect'
3857 4094 )
3858 4095 );
3859 - exit;
4096 + exit( 0 );
3860 4097 }
3861 4098 }
3862 4099
3863 4100 /*
@@ -3892,8 +4129,28 @@
3892 4129 * Done!
3893 4130 */
3894 4131
3895 4132 /**
4133 + * Build the user-facing description stored alongside a registration error code.
4134 + *
4135 + * @since 16.2
4136 + *
4137 + * @param string $error_code The WP_Error code.
4138 + * @param string $message The WP_Error message.
4139 + * @return string The description, empty when the message is not user-facing copy.
4140 + */
4141 + public static function get_registration_error_description( $error_code, $message ) {
4142 + // Manager::validate_remote_register_response() does not always put user-facing copy in the
4143 + // message slot: wpcom_5??, wpcom_408 and wpcom_bad_response store the HTTP status there,
4144 + // and jetpack_id stores the raw response body, which can also overflow the state cookie.
4145 + if ( 'jetpack_id' === $error_code || is_numeric( $message ) ) {
4146 + return '';
4147 + }
4148 +
4149 + return mb_substr( (string) $message, 0, 250 );
4150 + }
4151 +
4152 + /**
3896 4153 * Handles the page load events for the Jetpack admin page
3897 4154 */
3898 4155 public function admin_page_load() {
3899 4156 $error = false;
@@ -3929,10 +4186,11 @@
3929 4186 $registered = static::connection()->try_registration();
3930 4187 if ( is_wp_error( $registered ) ) {
3931 4188 $error = $registered->get_error_code();
3932 4189 self::state( 'error', $error );
3933 - self::state( 'error', $registered->get_error_message() );
3934 4190
4191 + self::state( 'error_description', self::get_registration_error_description( $error, $registered->get_error_message() ) );
4192 +
3935 4193 /**
3936 4194 * Jetpack registration Error.
3937 4195 *
3938 4196 * @since 7.5.0
@@ -3956,12 +4214,8 @@
3956 4214 do_action( 'jetpack_connection_register_success', $from );
3957 4215
3958 4216 $url = $this->build_connect_url( true, $redirect, $from );
3959 4217
3960 - if ( ! empty( $_GET['onboarding'] ) ) {
3961 - $url = add_query_arg( 'onboarding', rawurlencode_deep( wp_unslash( $_GET['onboarding'] ) ), $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3962 - }
3963 -
3964 4218 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
3965 4219 $url = add_query_arg( 'auth_approved', 'true', $url );
3966 4220 }
3967 4221
@@ -3966,9 +4220,9 @@
3966 4220 }
3967 4221
3968 4222 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
3969 4223 wp_safe_redirect( $url );
3970 - exit;
4224 + exit( 0 );
3971 4225 case 'activate':
3972 4226 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3973 4227 $error = 'cheatin';
3974 4228 break;
@@ -3982,9 +4236,9 @@
3982 4236 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
3983 4237 }
3984 4238 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3985 4239 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3986 - exit;
4240 + exit( 0 );
3987 4241 case 'activate_default_modules':
3988 4242 check_admin_referer( 'activate_default_modules' );
3989 4243 self::log( 'activate_default_modules' );
3990 4244 self::restate();
@@ -3992,9 +4246,9 @@
3992 4246 $max_version = isset( $_GET['max_version'] ) ? sanitize_text_field( wp_unslash( $_GET['max_version'] ) ) : false;
3993 4247 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_GET['other_modules'] ) ) : array();
3994 4248 self::activate_default_modules( $min_version, $max_version, $other_modules );
3995 4249 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3996 - exit;
4250 + exit( 0 );
3997 4251 case 'disconnect':
3998 4252 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3999 4253 $error = 'cheatin';
4000 4254 break;
@@ -4003,9 +4257,9 @@
4003 4257 check_admin_referer( 'jetpack-disconnect' );
4004 4258 self::log( 'disconnect' );
4005 4259 self::disconnect();
4006 4260 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4007 - exit;
4261 + exit( 0 );
4008 4262 case 'reconnect':
4009 4263 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4010 4264 $error = 'cheatin';
4011 4265 break;
@@ -4016,9 +4270,9 @@
4016 4270 self::disconnect();
4017 4271
4018 4272 add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4019 4273 wp_safe_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4020 - exit;
4274 + exit( 0 );
4021 4275 case 'deactivate':
4022 4276 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4023 4277 $error = 'cheatin';
4024 4278 break;
@@ -4032,9 +4286,9 @@
4032 4286 self::state( 'message', 'module_deactivated' );
4033 4287 }
4034 4288 self::state( 'module', $modules );
4035 4289 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4036 - exit;
4290 + exit( 0 );
4037 4291 case 'unlink':
4038 4292 $redirect = isset( $_GET['redirect'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect'] ) ) : '';
4039 4293 check_admin_referer( 'jetpack-unlink' );
4040 4294 self::log( 'unlink' );
@@ -4044,32 +4298,9 @@
4044 4298 wp_safe_redirect( admin_url() );
4045 4299 } else {
4046 4300 wp_safe_redirect( self::admin_url( array( 'page' => rawurlencode( $redirect ) ) ) );
4047 4301 }
4048 - exit;
4049 - case 'onboard':
4050 - if ( ! current_user_can( 'manage_options' ) ) {
4051 - wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4052 - } else {
4053 - self::create_onboarding_token();
4054 - $url = $this->build_connect_url( true );
4055 -
4056 - $token = Jetpack_Options::get_option( 'onboarding' );
4057 -
4058 - if ( false !== ( $token ) ) {
4059 - $url = add_query_arg( 'onboarding', $token, $url );
4060 - }
4061 -
4062 - $calypso_env = $this->get_calypso_env();
4063 - if ( ! empty( $calypso_env ) ) {
4064 - $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4065 - }
4066 -
4067 - add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
4068 - wp_safe_redirect( $url );
4069 - exit;
4070 - }
4071 - exit;
4302 + exit( 0 );
4072 4303 default:
4073 4304 /**
4074 4305 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4075 4306 *
@@ -4085,9 +4316,10 @@
4085 4316 if ( ! $error ) {
4086 4317 self::activate_new_modules( true );
4087 4318 }
4088 4319
4089 - $message_code = self::state( 'message' );
4320 + $activated_manage = false;
4321 + $message_code = self::state( 'message' );
4090 4322 if ( self::state( 'optin-manage' ) ) {
4091 4323 $activated_manage = $message_code;
4092 4324 $message_code = 'jetpack-manage';
4093 4325 }
@@ -4226,9 +4458,9 @@
4226 4458 $module_slugs[] = $module_slug;
4227 4459 $module_names[] = "<strong>{$module['name']}</strong>";
4228 4460 }
4229 4461
4230 - $module_slugs = join( ',', $module_slugs );
4462 + $module_slugs = implode( ',', $module_slugs );
4231 4463 ?>
4232 4464 <div id="message" class="jetpack-message jetpack-err">
4233 4465 <div class="squeezer">
4234 4466 <h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
@@ -4261,17 +4493,19 @@
4261 4493 count( $privacy_checks ),
4262 4494 '%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4263 4495 'jetpack'
4264 4496 ),
4265 - wp_nonce_url(
4266 - self::admin_url(
4267 - array(
4268 - 'page' => 'jetpack',
4269 - 'action' => 'deactivate',
4270 - 'module' => rawurlencode( $module_slugs ),
4271 - )
4272 - ),
4273 - "jetpack_deactivate-$module_slugs"
4497 + esc_url(
4498 + wp_nonce_url(
4499 + self::admin_url(
4500 + array(
4501 + 'page' => 'jetpack',
4502 + 'action' => 'deactivate',
4503 + 'module' => rawurlencode( $module_slugs ),
4504 + )
4505 + ),
4506 + "jetpack_deactivate-$module_slugs"
4507 + )
4274 4508 ),
4275 4509 esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4276 4510 ),
4277 4511 array(
@@ -4289,37 +4523,8 @@
4289 4523 endif;
4290 4524 }
4291 4525
4292 4526 /**
4293 - * We can't always respond to a signed XML-RPC request with a
4294 - * helpful error message. In some circumstances, doing so could
4295 - * leak information.
4296 - *
4297 - * Instead, track that the error occurred via a Jetpack_Option,
4298 - * and send that data back in the heartbeat.
4299 - * All this does is increment a number, but it's enough to find
4300 - * trends.
4301 - *
4302 - * @param WP_Error $xmlrpc_error The error produced during
4303 - * signature validation.
4304 - */
4305 - public function track_xmlrpc_error( $xmlrpc_error ) {
4306 - $code = is_wp_error( $xmlrpc_error )
4307 - ? $xmlrpc_error->get_error_code()
4308 - : 'should-not-happen';
4309 -
4310 - $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4311 - if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4312 - // No need to update the option if we already have
4313 - // this code stored.
4314 - return;
4315 - }
4316 - $xmlrpc_errors[ $code ] = true;
4317 -
4318 - Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4319 - }
4320 -
4321 - /**
4322 4527 * Initialize the jetpack stats instance only when needed
4323 4528 *
4324 4529 * @return void
4325 4530 */
@@ -4417,9 +4622,9 @@
4417 4622 if ( is_network_admin() ) {
4418 4623 $url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4419 4624 }
4420 4625
4421 - $calypso_env = self::get_calypso_env();
4626 + $calypso_env = ( new Host() )->get_calypso_env();
4422 4627
4423 4628 if ( ! empty( $calypso_env ) ) {
4424 4629 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4425 4630 }
@@ -4442,9 +4647,9 @@
4442 4647 return $this->build_connect_url( $raw, $redirect, $from, true );
4443 4648 }
4444 4649 }
4445 4650
4446 - $url = $this->build_authorize_url( $redirect );
4651 + $url = ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4447 4652 }
4448 4653
4449 4654 if ( $from ) {
4450 4655 $url = add_query_arg( 'from', $from, $url );
@@ -4469,66 +4674,30 @@
4469 4674 * @param null $deprecated Deprecated since Jetpack 10.9.
4470 4675 *
4471 4676 * @todo Update default value for redirect since the called function expects a string.
4472 4677 *
4678 + * @deprecated 13.4
4679 + *
4473 4680 * @return mixed|void
4474 4681 */
4475 4682 public static function build_authorize_url( $redirect = false, $deprecated = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
4683 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::build_authorize_url' );
4476 4684
4477 - add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4478 - add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4479 -
4480 - $c8n = self::connection();
4481 - $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4482 -
4483 - remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4484 - remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4485 -
4486 - /**
4487 - * Filter the URL used when authorizing a user to a WordPress.com account.
4488 - *
4489 - * @since 8.9.0
4490 - *
4491 - * @param string $url Connection URL.
4492 - */
4493 - return apply_filters( 'jetpack_build_authorize_url', $url );
4685 + return ( new Authorize_Redirect( static::connection() ) )->build_authorize_url( $redirect );
4494 4686 }
4495 4687
4496 4688 /**
4497 4689 * Filters the connection URL parameter array.
4498 4690 *
4691 + * @deprecated 13.4
4692 + *
4499 4693 * @param array $args default URL parameters used by the package.
4500 4694 * @return array the modified URL arguments array.
4501 4695 */
4502 4696 public static function filter_connect_request_body( $args ) {
4503 - if (
4504 - Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4505 - && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4506 - ) {
4507 - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4508 - $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4509 - ? $gp_locale->slug
4510 - : '';
4511 - }
4697 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_request_body' );
4512 4698
4513 - $tracking = new Tracking();
4514 - $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4515 -
4516 - $args = array_merge(
4517 - $args,
4518 - array(
4519 - '_ui' => $tracks_identity['_ui'],
4520 - '_ut' => $tracks_identity['_ut'],
4521 - )
4522 - );
4523 -
4524 - $calypso_env = self::get_calypso_env();
4525 -
4526 - if ( ! empty( $calypso_env ) ) {
4527 - $args['calypso_env'] = $calypso_env;
4528 - }
4529 -
4530 - return $args;
4699 + return Authorize_Redirect::filter_connect_request_body( $args );
4531 4700 }
4532 4701
4533 4702 /**
4534 4703 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
@@ -4565,41 +4734,19 @@
4565 4734 return $current_user_connection_data;
4566 4735 }
4567 4736
4568 4737 /**
4569 - * Filters the URL that will process the connection data. It can be different from the URL
4570 - * that we send the user to after everything is done.
4571 - *
4572 - * @param String $processing_url the default redirect URL used by the package.
4573 - * @return String the modified URL.
4574 - *
4575 - * @deprecated since Jetpack 9.5.0
4576 - */
4577 - public static function filter_connect_processing_url( $processing_url ) {
4578 - _deprecated_function( __METHOD__, 'jetpack-9.5' );
4579 -
4580 - $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4581 - return $processing_url;
4582 - }
4583 -
4584 - /**
4585 4738 * Filters the redirection URL that is used for connect requests. The redirect
4586 4739 * URL should return the user back to the Jetpack console.
4587 4740 *
4741 + * @deprecated 13.4
4742 + *
4588 4743 * @param String $redirect the default redirect URL used by the package.
4589 4744 * @return String the modified URL.
4590 4745 */
4591 4746 public static function filter_connect_redirect_url( $redirect ) {
4592 - $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4593 - $redirect = $redirect
4594 - ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4595 - : $jetpack_admin_page;
4596 -
4597 - if ( isset( $_REQUEST['is_multisite'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- not making a site change here.
4598 - $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4599 - }
4600 -
4601 - return $redirect;
4747 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Authorize_Redirect::filter_connect_redirect_url' );
4748 + return Authorize_Redirect::filter_connect_redirect_url( $redirect );
4602 4749 }
4603 4750
4604 4751 /**
4605 4752 * This action fires at the beginning of the Manager::authorize method.
@@ -4663,11 +4810,8 @@
4663 4810 *
4664 4811 * @param array $data The request data.
4665 4812 */
4666 4813 public static function authorize_ending_authorized( $data ) {
4667 - // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4668 - self::invalidate_onboarding_token();
4669 -
4670 4814 // If redirect_uri is SSO, ensure SSO module is enabled.
4671 4815 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4672 4816
4673 4817 /** This filter is documented in class.jetpack-cli.php */
@@ -4791,13 +4935,9 @@
4791 4935 *
4792 4936 * @return int 0 if the same sort or (+/-) to indicate which is greater.
4793 4937 */
4794 4938 public static function sort_modules( $a, $b ) {
4795 - if ( $a['sort'] === $b['sort'] ) {
4796 - return 0;
4797 - }
4798 -
4799 - return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4939 + return $a['sort'] <=> $b['sort'];
4800 4940 }
4801 4941
4802 4942 /**
4803 4943 * Recheck SSL status for use via an AJAX call.
@@ -4811,10 +4951,12 @@
4811 4951 $result = self::permit_ssl( true );
4812 4952 wp_send_json(
4813 4953 array(
4814 4954 'enabled' => $result,
4815 - 'message' => get_transient( 'jetpack_https_test_message' ),
4816 - )
4955 + 'message' => self::get_ssl_test_message(),
4956 + ),
4957 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
4958 + JSON_UNESCAPED_SLASHES
4817 4959 );
4818 4960 }
4819 4961
4820 4962 /* Client API */
@@ -4821,8 +4963,10 @@
4821 4963
4822 4964 /**
4823 4965 * Verify the onboarding token.
4824 4966 *
4967 + * @deprecated since 13.9
4968 + *
4825 4969 * @param array $token_data Token data.
4826 4970 * @param string $token Token value.
4827 4971 * @param string $request_data JSON-encoded request data.
4828 4972 *
@@ -4828,8 +4972,9 @@
4828 4972 *
4829 4973 * @return mixed
4830 4974 */
4831 4975 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
4976 + _deprecated_function( __METHOD__, '13.9' );
4832 4977 // Default to a blog token.
4833 4978 $token_type = 'blog';
4834 4979
4835 4980 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
@@ -4857,9 +5002,9 @@
4857 5002 $jp_user = get_user_by( 'email', $jpo_user );
4858 5003 if ( is_a( $jp_user, 'WP_User' ) ) {
4859 5004 wp_set_current_user( $jp_user->ID );
4860 5005 $user_can = is_multisite()
4861 - ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5006 + ? current_user_can_for_site( get_current_blog_id(), 'manage_options' )
4862 5007 : current_user_can( 'manage_options' );
4863 5008 if ( $user_can ) {
4864 5009 $token_type = 'user';
4865 5010 $token->external_user_id = $jp_user->ID;
@@ -4876,11 +5021,13 @@
4876 5021
4877 5022 /**
4878 5023 * Create a random secret for validating onboarding payload
4879 5024 *
5025 + * @deprecated since 13.9
4880 5026 * @return string Secret token
4881 5027 */
4882 5028 public static function create_onboarding_token() {
5029 + _deprecated_function( __METHOD__, '13.9' );
4883 5030 $token = Jetpack_Options::get_option( 'onboarding' );
4884 5031 if ( false === ( $token ) ) {
4885 5032 $token = wp_generate_password( 32, false );
4886 5033 Jetpack_Options::update_option( 'onboarding', $token );
@@ -4891,11 +5038,13 @@
4891 5038
4892 5039 /**
4893 5040 * Remove the onboarding token
4894 5041 *
5042 + * @deprecated since 13.9
4895 5043 * @return bool True on success, false on failure
4896 5044 */
4897 5045 public static function invalidate_onboarding_token() {
5046 + _deprecated_function( __METHOD__, '13.9' );
4898 5047 return Jetpack_Options::delete_option( 'onboarding' );
4899 5048 }
4900 5049
4901 5050 /**
@@ -4900,8 +5049,10 @@
4900 5049
4901 5050 /**
4902 5051 * Validate an onboarding token for a specific action
4903 5052 *
5053 + * @deprecated since 13.9
5054 + *
4904 5055 * @param string $token Onboarding token.
4905 5056 * @param string $action Action name.
4906 5057 *
4907 5058 * @return boolean True if token/action pair is accepted, false if not
@@ -4906,8 +5057,9 @@
4906 5057 *
4907 5058 * @return boolean True if token/action pair is accepted, false if not
4908 5059 */
4909 5060 public static function validate_onboarding_token_action( $token, $action ) {
5061 + _deprecated_function( __METHOD__, '13.9' );
4910 5062 // Compare tokens, bail if tokens do not match.
4911 5063 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4912 5064 return false;
4913 5065 }
@@ -4933,39 +5085,41 @@
4933 5085 * @return boolean
4934 5086 * @since 2.3.3
4935 5087 */
4936 5088 public static function permit_ssl( $force_recheck = false ) {
4937 - // Do some fancy tests to see if ssl is being supported.
4938 - if ( ! $force_recheck ) {
4939 - $ssl = get_transient( 'jetpack_https_test' );
5089 + if ( ! method_exists( Heartbeat::class, 'permit_ssl' ) ) {
5090 + // Skip the SSL-fail notice when the check cannot run.
5091 + return true;
4940 5092 }
4941 5093
4942 - if ( $force_recheck || false === $ssl ) {
4943 - $message = '';
4944 - if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4945 - $ssl = 0;
4946 - } else {
4947 - $ssl = 1;
5094 + return Heartbeat::permit_ssl( $force_recheck );
5095 + }
4948 5096
4949 - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4950 - $ssl = 0;
4951 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4952 - } else {
4953 - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4954 - if ( is_wp_error( $response ) ) {
4955 - $ssl = 0;
4956 - $message = __( 'WordPress reports no SSL support', 'jetpack' );
4957 - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4958 - $ssl = 0;
4959 - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4960 - }
4961 - }
4962 - }
4963 - set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4964 - set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5097 + /**
5098 + * Returns a localized message describing the last SSL connectivity failure, if any.
5099 + *
5100 + * The Connection package's canonical SSL check stores a neutral reason code; this maps it to
5101 + * a translated, `jetpack`-domain message for display in the admin notice and AJAX recheck.
5102 + *
5103 + * @since 16.1
5104 + *
5105 + * @return string The localized message, or an empty string when there is no failure.
5106 + */
5107 + public static function get_ssl_test_message() {
5108 + if ( ! method_exists( Heartbeat::class, 'get_ssl_test_error' ) ) {
5109 + return '';
4965 5110 }
4966 5111
4967 - return (bool) $ssl;
5112 + $error = Heartbeat::get_ssl_test_error();
5113 +
5114 + switch ( $error['code'] ) {
5115 + case 'no_ssl_support':
5116 + return __( 'WordPress reports no SSL support', 'jetpack' );
5117 + case 'bad_response':
5118 + return __( 'Response was not OK: ', 'jetpack' ) . $error['detail'];
5119 + default:
5120 + return '';
5121 + }
4968 5122 }
4969 5123
4970 5124 /**
4971 5125 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
@@ -4984,9 +5138,9 @@
4984 5138 <p><?php esc_html_e( 'Your site could not connect to WordPress.com via HTTPS. This could be due to any number of reasons, including faulty SSL certificates, misconfigured or missing SSL libraries, or network issues.', 'jetpack' ); ?></p>
4985 5139 <p>
4986 5140 <?php esc_html_e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
4987 5141 <a href="#" id="jetpack-recheck-ssl-button"><?php esc_html_e( 'Try again', 'jetpack' ); ?></a>
4988 - <span id="jetpack-recheck-ssl-output"><?php echo esc_html( get_transient( 'jetpack_https_test_message' ) ); ?></span>
5142 + <span id="jetpack-recheck-ssl-output"><?php echo esc_html( self::get_ssl_test_message() ); ?></span>
4989 5143 </p>
4990 5144 <p>
4991 5145 <?php
4992 5146 printf(
@@ -5005,18 +5159,18 @@
5005 5159 <script type="text/javascript">
5006 5160 jQuery( document ).ready( function( $ ) {
5007 5161 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5008 5162 var $this = $( this );
5009 - $this.html( <?php echo wp_json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5163 + $this.html( <?php echo wp_json_encode( esc_html__( 'Checking', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5010 5164 $( '#jetpack-recheck-ssl-output' ).html( '' );
5011 5165 e.preventDefault();
5012 - var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce ); ?> };
5166 + var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': <?php echo wp_json_encode( $ajax_nonce, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> };
5013 5167 $.post( ajaxurl, data )
5014 5168 .done( function( response ) {
5015 5169 if ( response.enabled ) {
5016 5170 $( '#jetpack-ssl-warning' ).hide();
5017 5171 } else {
5018 - this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5172 + this.html( <?php echo wp_json_encode( esc_html__( 'Try again', 'jetpack' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
5019 5173 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5020 5174 }
5021 5175 }.bind( $this ) );
5022 5176 } );
@@ -5042,26 +5196,8 @@
5042 5196 return $jetpack->connection_manager;
5043 5197 }
5044 5198
5045 5199 /**
5046 - * Creates two secret tokens and the end of life timestamp for them.
5047 - *
5048 - * Note these tokens are unique per call, NOT static per site for connecting.
5049 - *
5050 - * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5051 - *
5052 - * @since 2.6
5053 - * @param String $action The action name.
5054 - * @param Integer $user_id The user identifier.
5055 - * @param Integer $exp Expiration time in seconds.
5056 - * @return array
5057 - */
5058 - public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5059 - _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5060 - return self::connection()->generate_secrets( $action, $user_id, $exp );
5061 - }
5062 -
5063 - /**
5064 5200 * Get verification secrets.
5065 5201 *
5066 5202 * @param string $action Action name.
5067 5203 * @param int $user_id User ID.
@@ -5082,35 +5218,8 @@
5082 5218 return $secrets;
5083 5219 }
5084 5220
5085 5221 /**
5086 - * Register a connection.
5087 - *
5088 - * @deprecated Jetpack 9.7.0
5089 - * @see Automattic\Jetpack\Connection\Manager::try_registration()
5090 - *
5091 - * @return bool|WP_Error
5092 - */
5093 - public static function register() {
5094 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5095 - return static::connection()->try_registration( false );
5096 - }
5097 -
5098 - /**
5099 - * Filters the registration request body to include tracking properties.
5100 - *
5101 - * @deprecated Jetpack 9.7.0
5102 - * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5103 - *
5104 - * @param array $properties Token request properties.
5105 - * @return array amended properties.
5106 - */
5107 - public static function filter_register_request_body( $properties ) {
5108 - _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5109 - return Connection_Utils::filter_register_request_body( $properties );
5110 - }
5111 -
5112 - /**
5113 5222 * Filters the token request body to include tracking properties.
5114 5223 *
5115 5224 * @param array $properties Token request properties.
5116 5225 *
@@ -5131,9 +5240,9 @@
5131 5240
5132 5241 /**
5133 5242 * If the db version is showing something other that what we've got now, bump it to current.
5134 5243 *
5135 - * @return bool: True if the option was incorrect and updated, false if nothing happened.
5244 + * @return bool True if the option was incorrect and updated, false if nothing happened.
5136 5245 */
5137 5246 public static function maybe_set_version_option() {
5138 5247 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5139 5248 if ( JETPACK__VERSION !== $version ) {
@@ -5151,21 +5260,8 @@
5151 5260
5152 5261 /* Client Server API */
5153 5262
5154 5263 /**
5155 - * Loads the Jetpack XML-RPC client.
5156 - * No longer necessary, as the XML-RPC client will be automagically loaded.
5157 - *
5158 - * Note: we cannot remove this function yet as it is used in this plugin:
5159 - * https://wordpress.org/plugins/jetpack-subscription-form/
5160 - *
5161 - * @deprecated since 7.7.0
5162 - */
5163 - public static function load_xml_rpc_client() {
5164 - _deprecated_function( __METHOD__, 'jetpack-7.7' );
5165 - }
5166 -
5167 - /**
5168 5264 * State is passed via cookies from one request to the next, but never to subsequent requests.
5169 5265 * SET: state( $key, $value );
5170 5266 * GET: $value = state( $key );
5171 5267 *
@@ -5239,20 +5335,8 @@
5239 5335
5240 5336 self::state( 'privacy_checks', $privacy_checks );
5241 5337 }
5242 5338
5243 - /**
5244 - * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5245 - *
5246 - * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5247 - *
5248 - * @param string $url WordPress.com static resource URL.
5249 - */
5250 - public static function staticize_subdomain( $url ) {
5251 - _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5252 - return Assets::staticize_subdomain( $url );
5253 - }
5254 -
5255 5339 /* JSON API Authorization */
5256 5340
5257 5341 /**
5258 5342 * Handles the login action for Authorizing the JSON API
@@ -5257,13 +5341,14 @@
5257 5341 /**
5258 5342 * Handles the login action for Authorizing the JSON API
5259 5343 */
5260 5344 public function login_form_json_api_authorization() {
5261 - $this->verify_json_api_authorization_request();
5345 + $authorize_json_api = new Authorize_Json_Api();
5346 + $authorize_json_api->verify_json_api_authorization_request();
5262 5347
5263 - add_action( 'wp_login', array( $this, 'store_json_api_authorization_token' ), 10, 2 );
5348 + add_action( 'wp_login', array( $authorize_json_api, 'store_json_api_authorization_token' ), 10, 2 );
5264 5349
5265 - add_action( 'login_message', array( $this, 'login_message_json_api_authorization' ) );
5350 + add_action( 'login_message', array( $authorize_json_api, 'login_message_json_api_authorization' ) );
5266 5351 add_action( 'login_form', array( $this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5267 5352 add_filter( 'site_url', array( $this, 'post_login_form_to_signed_url' ), 10, 3 );
5268 5353 }
5269 5354
@@ -5301,16 +5386,17 @@
5301 5386
5302 5387 /**
5303 5388 * If someone logs in to approve API access, store the Access Code in usermeta.
5304 5389 *
5390 + * @deprecated 13.4
5391 + *
5305 5392 * @param string $user_login Unused.
5306 5393 * @param WP_User $user User logged in.
5307 5394 */
5308 5395 public function store_json_api_authorization_token( $user_login, $user ) {
5309 - add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5310 - add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_public_api_domain' ) );
5311 - $token = wp_generate_password( 32, false );
5312 - update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5396 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::store_json_api_authorization_token' );
5397 +
5398 + return ( new Authorize_Json_Api() )->store_json_api_authorization_token( $user_login, $user );
5313 5399 }
5314 5400
5315 5401 /**
5316 5402 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
@@ -5316,23 +5402,29 @@
5316 5402 * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5317 5403 *
5318 5404 * To be used with a filter of allowed domains for a redirect.
5319 5405 *
5406 + * @deprecated 13.4
5407 + *
5320 5408 * @param array $domains Allowed WP.com Environments.
5321 5409 */
5322 5410 public function allow_wpcom_public_api_domain( $domains ) {
5323 - $domains[] = 'public-api.wordpress.com';
5324 - return $domains;
5411 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Status\\Host::allow_wpcom_public_api_domain' );
5412 +
5413 + return Host::allow_wpcom_public_api_domain( $domains );
5325 5414 }
5326 5415
5327 5416 /**
5328 5417 * Check if the redirect is encoded.
5329 5418 *
5419 + * @deprecated 13.4
5420 + *
5330 5421 * @param string $redirect_url Redirect URL.
5331 5422 *
5332 5423 * @return bool If redirect has been encoded.
5333 5424 */
5334 5425 public static function is_redirect_encoded( $redirect_url ) {
5426 + _deprecated_function( __METHOD__, 'jetpack-13.4' );
5335 5427 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5336 5428 }
5337 5429
5338 5430 /**
@@ -5350,8 +5442,10 @@
5350 5442
5351 5443 /**
5352 5444 * Add the Access Code details to the public-api.wordpress.com redirect.
5353 5445 *
5446 + * @deprecated 13.4
5447 + *
5354 5448 * @param string $redirect_to URL.
5355 5449 * @param string $original_redirect_to URL.
5356 5450 * @param WP_User $user WP_User for the redirect.
5357 5451 *
@@ -5357,23 +5451,18 @@
5357 5451 *
5358 5452 * @return string
5359 5453 */
5360 5454 public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5361 - return add_query_arg(
5362 - urlencode_deep(
5363 - array(
5364 - 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5365 - 'jetpack-user-id' => (int) $user->ID,
5366 - 'jetpack-state' => $this->json_api_authorization_request['state'],
5367 - )
5368 - ),
5369 - $redirect_to
5370 - );
5455 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::add_token_to_login_redirect_json_api_authorization' );
5456 +
5457 + return ( new Authorize_Json_Api() )->add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user );
5371 5458 }
5372 5459
5373 5460 /**
5374 5461 * Verifies the request by checking the signature
5375 5462 *
5463 + * @deprecated 13.4
5464 + *
5376 5465 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5377 5466 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5378 5467 *
5379 5468 * @param null|array $environment Value to override $_REQUEST.
@@ -5378,194 +5467,24 @@
5378 5467 *
5379 5468 * @param null|array $environment Value to override $_REQUEST.
5380 5469 */
5381 5470 public function verify_json_api_authorization_request( $environment = null ) {
5382 - $environment = $environment === null
5383 - ? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function.
5384 - : $environment;
5471 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::verify_json_api_authorization_request' );
5385 5472
5386 - list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
5387 - $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
5388 - if ( ! $token || empty( $token->secret ) ) {
5389 - wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5390 - }
5391 -
5392 - $die_error = __( 'Someone may be trying to trick you into giving them access to your site. Or it could be you just encountered a bug :). Either way, please close this window.', 'jetpack' );
5393 -
5394 - // Host has encoded the request URL, probably as a result of a bad http => https redirect.
5395 - if ( self::is_redirect_encoded( esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no site changes, we're erroring out.
5396 - /**
5397 - * Jetpack authorisation request Error.
5398 - *
5399 - * @since 7.5.0
5400 - */
5401 - do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5402 - $die_error = sprintf(
5403 - /* translators: %s is a URL */
5404 - __( 'Your site is incorrectly double-encoding redirects from http to https. This is preventing Jetpack from authenticating your connection. Please visit our <a href="%s">support page</a> for details about how to resolve this.', 'jetpack' ),
5405 - Redirect::get_url( 'jetpack-support-double-encoding' )
5406 - );
5407 - }
5408 -
5409 - $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5410 -
5411 - if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5412 - $signature = $jetpack_signature->sign_request(
5413 - $environment['token'],
5414 - $environment['timestamp'],
5415 - $environment['nonce'],
5416 - '',
5417 - 'GET',
5418 - $environment['jetpack_json_api_original_query'],
5419 - null,
5420 - true
5421 - );
5422 - } else {
5423 - $signature = $jetpack_signature->sign_current_request(
5424 - array(
5425 - 'body' => null,
5426 - 'method' => 'GET',
5427 - )
5428 - );
5429 - }
5430 -
5431 - if ( ! $signature ) {
5432 - wp_die(
5433 - wp_kses(
5434 - $die_error,
5435 - array(
5436 - 'a' => array(
5437 - 'href' => array(),
5438 - ),
5439 - )
5440 - )
5441 - );
5442 - } elseif ( is_wp_error( $signature ) ) {
5443 - wp_die(
5444 - wp_kses(
5445 - $die_error,
5446 - array(
5447 - 'a' => array(
5448 - 'href' => array(),
5449 - ),
5450 - )
5451 - )
5452 - );
5453 - } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5454 - if ( is_ssl() ) {
5455 - // If we signed an HTTP request on the Jetpack Servers, but got redirected to HTTPS by the local blog, check the HTTP signature as well.
5456 - $signature = $jetpack_signature->sign_current_request(
5457 - array(
5458 - 'scheme' => 'http',
5459 - 'body' => null,
5460 - 'method' => 'GET',
5461 - )
5462 - );
5463 - if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5464 - wp_die(
5465 - wp_kses(
5466 - $die_error,
5467 - array(
5468 - 'a' => array(
5469 - 'href' => array(),
5470 - ),
5471 - )
5472 - )
5473 - );
5474 - }
5475 - } else {
5476 - wp_die(
5477 - wp_kses(
5478 - $die_error,
5479 - array(
5480 - 'a' => array(
5481 - 'href' => array(),
5482 - ),
5483 - )
5484 - )
5485 - );
5486 - }
5487 - }
5488 -
5489 - $timestamp = (int) $environment['timestamp'];
5490 - $nonce = stripslashes( (string) $environment['nonce'] );
5491 -
5492 - if ( ! $this->connection_manager ) {
5493 - $this->connection_manager = new Connection_Manager();
5494 - }
5495 -
5496 - if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
5497 - // De-nonce the nonce, at least for 5 minutes.
5498 - // We have to reuse this nonce at least once (used the first time when the initial request is made, used a second time when the login form is POSTed).
5499 - $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5500 - if ( $old_nonce_time < time() - 300 ) {
5501 - wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
5502 - }
5503 - }
5504 -
5505 - $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
5506 - $data_filters = array(
5507 - 'state' => 'opaque',
5508 - 'client_id' => 'int',
5509 - 'client_title' => 'string',
5510 - 'client_image' => 'url',
5511 - );
5512 -
5513 - foreach ( $data_filters as $key => $sanitation ) {
5514 - if ( ! isset( $data->$key ) ) {
5515 - wp_die(
5516 - wp_kses(
5517 - $die_error,
5518 - array(
5519 - 'a' => array(
5520 - 'href' => array(),
5521 - ),
5522 - )
5523 - )
5524 - );
5525 - }
5526 -
5527 - switch ( $sanitation ) {
5528 - case 'int':
5529 - $this->json_api_authorization_request[ $key ] = (int) $data->$key;
5530 - break;
5531 - case 'opaque':
5532 - $this->json_api_authorization_request[ $key ] = (string) $data->$key;
5533 - break;
5534 - case 'string':
5535 - $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
5536 - break;
5537 - case 'url':
5538 - $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
5539 - break;
5540 - }
5541 - }
5542 -
5543 - if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5544 - wp_die(
5545 - wp_kses(
5546 - $die_error,
5547 - array(
5548 - 'a' => array(
5549 - 'href' => array(),
5550 - ),
5551 - )
5552 - )
5553 - );
5554 - }
5473 + return ( new Authorize_Json_Api() )->verify_json_api_authorization_request( $environment );
5555 5474 }
5556 5475
5557 5476 /**
5558 5477 * HTML for the JSON API authorization notice.
5559 5478 *
5479 + * @deprecated 13.4
5480 + *
5560 5481 * @return string
5561 5482 */
5562 5483 public function login_message_json_api_authorization() {
5563 - return '<p class="message">' . sprintf(
5564 - /* translators: Name/image of the client requesting authorization */
5565 - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
5566 - '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5567 - ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5484 + _deprecated_function( __METHOD__, 'jetpack-13.4', 'Automattic\\Jetpack\\Connection\\Authorize_Json_Api::login_message_json_api_authorization' );
5485 +
5486 + return ( new Authorize_Json_Api() )->login_message_json_api_authorization();
5568 5487 }
5569 5488
5570 5489 /**
5571 5490 * Get $content_width, but with a <s>twist</s> filter.
@@ -5609,32 +5528,23 @@
5609 5528
5610 5529 /**
5611 5530 * Checks if the site is currently in an identity crisis.
5612 5531 *
5532 + * Now delegates to the Connection package so this matches what the heartbeat itself reports.
5533 + * Note the package guards on `Connection\Manager::is_connected()` where this used to guard on
5534 + * `Jetpack::is_connection_ready()`, so the `jetpack_is_connection_ready` filter no longer applies.
5535 + *
5536 + * @deprecated 16.2
5537 + *
5613 5538 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
5614 5539 */
5615 5540 public static function check_identity_crisis() {
5616 - if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
5617 - return false;
5618 - }
5541 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Identity_Crisis::check_identity_crisis' );
5619 5542
5620 - return Jetpack_Options::get_option( 'sync_error_idc' );
5543 + return Identity_Crisis::check_identity_crisis();
5621 5544 }
5622 5545
5623 5546 /**
5624 - * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
5625 - *
5626 - * @since 4.4.0
5627 - * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
5628 - *
5629 - * @return bool
5630 - */
5631 - public static function validate_sync_error_idc_option() {
5632 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' );
5633 - return Identity_Crisis::validate_sync_error_idc_option();
5634 - }
5635 -
5636 - /**
5637 5547 * Normalizes a url by doing three things:
5638 5548 * - Strips protocol
5639 5549 * - Strips www
5640 5550 * - Adds a trailing slash
@@ -5655,35 +5565,8 @@
5655 5565 return $url;
5656 5566 }
5657 5567
5658 5568 /**
5659 - * Gets the value that is to be saved in the jetpack_sync_error_idc option.
5660 - *
5661 - * @since 4.4.0
5662 - * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
5663 - * @deprecated 9.8.0 Use \\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option
5664 - *
5665 - * @param array $response HTTP response.
5666 - * @return array Array of the local urls, wpcom urls, and error code
5667 - */
5668 - public static function get_sync_error_idc_option( $response = array() ) {
5669 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' );
5670 - return Identity_Crisis::get_sync_error_idc_option( $response );
5671 - }
5672 -
5673 - /**
5674 - * Returns the value of the jetpack_sync_idc_optin filter, or constant.
5675 - * If set to true, the site will be put into staging mode.
5676 - *
5677 - * @since 4.3.2
5678 - * @return bool
5679 - */
5680 - public static function sync_idc_optin() {
5681 - _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' );
5682 - return Identity_Crisis::sync_idc_optin();
5683 - }
5684 -
5685 - /**
5686 5569 * Maybe Use a .min.css stylesheet, maybe not.
5687 5570 *
5688 5571 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
5689 5572 *
@@ -5704,9 +5587,9 @@
5704 5587 // Strip out the abspath.
5705 5588 $base = dirname( plugin_basename( $plugin ) );
5706 5589
5707 5590 // Short out on non-Jetpack assets.
5708 - if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
5591 + if ( ! str_starts_with( $base, 'jetpack/' ) ) {
5709 5592 return $url;
5710 5593 }
5711 5594
5712 5595 // File name parsing.
@@ -5746,15 +5629,15 @@
5746 5629 *
5747 5630 * @return mixed
5748 5631 */
5749 5632 public static function set_suffix_on_min( $src, $handle ) {
5750 - if ( false === strpos( $src, '.min.css' ) ) {
5633 + if ( ! is_string( $src ) || ! str_contains( $src, '.min.css' ) ) {
5751 5634 return $src;
5752 5635 }
5753 5636
5754 5637 if ( ! empty( self::$min_assets ) ) {
5755 5638 foreach ( self::$min_assets as $file => $path ) {
5756 - if ( false !== strpos( $src, $file ) ) {
5639 + if ( str_contains( $src, $file ) ) {
5757 5640 wp_style_add_data( $handle, 'suffix', '.min' );
5758 5641 return $src;
5759 5642 }
5760 5643 }
@@ -5784,8 +5667,10 @@
5784 5667 *
5785 5668 * Data passed in with the $data parameter will be available in the
5786 5669 * template file as $data['value']
5787 5670 *
5671 + * @html-template-var array $data
5672 + *
5788 5673 * @param string $template - Template file to load.
5789 5674 * @param array $data - Any data to pass along to the template.
5790 5675 * @return boolean - If template file was found.
5791 5676 **/
@@ -5803,8 +5688,19 @@
5803 5688 return false;
5804 5689 }
5805 5690
5806 5691 /**
5692 + * Register Jetpack-specific tests on the connection package's health test suite.
5693 + *
5694 + * @param \Automattic\Jetpack\Connection\Connection_Health_Tests $connection_tests The test suite instance.
5695 + */
5696 + public function register_jetpack_connection_tests( $connection_tests ) {
5697 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger/class-jetpack-cxn-tests.php';
5698 + $jetpack_tests = new Jetpack_Cxn_Tests();
5699 + $jetpack_tests->register_tests_on( $connection_tests );
5700 + }
5701 +
5702 + /**
5807 5703 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
5808 5704 */
5809 5705 public function deprecated_hooks() {
5810 5706 $filter_deprecated_list = array(
@@ -5931,13 +5827,8 @@
5931 5827 'jetpack_cache_plans' => array(
5932 5828 'replacement' => null,
5933 5829 'version' => 'jetpack-6.1.0',
5934 5830 ),
5935 -
5936 - 'jetpack_lazy_images_skip_image_with_atttributes' => array(
5937 - 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
5938 - 'version' => 'jetpack-6.5.0',
5939 - ),
5940 5831 'jetpack_enable_site_verification' => array(
5941 5832 'replacement' => null,
5942 5833 'version' => 'jetpack-6.5.0',
5943 5834 ),
@@ -6021,8 +5912,22 @@
6021 5912 'jetpack_are_blogging_prompts_enabled' => array(
6022 5913 'replacement' => null,
6023 5914 'version' => 'jetpack-11.8.0',
6024 5915 ),
5916 + 'jetpack_subscriptions_modal_enabled' => array(
5917 + 'replacement' => null,
5918 + 'version' => 'jetpack-12.7.0',
5919 + ),
5920 + 'jetpack_pre_connection_prompt_helpers' => array(
5921 + 'replacement' => null,
5922 + 'version' => 'jetpack-13.2.0',
5923 + ),
5924 + 'jetpack_contact_form_use_package' => array(
5925 + 'replacement' => null,
5926 + 'version' => 'jetpack-13.4.0',
5927 + ),
5928 + // jetpack_implode_frontend_css has been removed, but is not listed here. The updated behavior is exactly the only use of the filter.
5929 + // We can reassess formally deprecating it here later; for now, it would be noise with no functional difference.
6025 5930 );
6026 5931
6027 5932 foreach ( $filter_deprecated_list as $tag => $args ) {
6028 5933 if ( has_filter( $tag ) ) {
@@ -6125,9 +6030,9 @@
6125 6030 foreach ( $matches as $match ) {
6126 6031 $url = trim( $match['path'], "'\" \t" );
6127 6032
6128 6033 // If this is a data url, we don't want to mess with it.
6129 - if ( 'data:' === substr( $url, 0, 5 ) ) {
6034 + if ( str_starts_with( $url, 'data:' ) ) {
6130 6035 continue;
6131 6036 }
6132 6037
6133 6038 // If this is an absolute or protocol-agnostic url,
@@ -6153,113 +6058,8 @@
6153 6058 return $css;
6154 6059 }
6155 6060
6156 6061 /**
6157 - * This methods removes all of the registered css files on the front end
6158 - * from Jetpack in favor of using a single file. In effect "imploding"
6159 - * all the files into one file.
6160 - *
6161 - * Pros:
6162 - * - Uses only ONE css asset connection instead of 15
6163 - * - Saves a minimum of 56k
6164 - * - Reduces server load
6165 - * - Reduces time to first painted byte
6166 - *
6167 - * Cons:
6168 - * - Loads css for ALL modules. However all selectors are prefixed so it
6169 - * should not cause any issues with themes.
6170 - * - Plugins/themes dequeuing styles no longer do anything. See
6171 - * jetpack_implode_frontend_css filter for a workaround
6172 - *
6173 - * For some situations developers may wish to disable css imploding and
6174 - * instead operate in legacy mode where each file loads seperately and
6175 - * can be edited individually or dequeued. This can be accomplished with
6176 - * the following line:
6177 - *
6178 - * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6179 - *
6180 - * @param bool $travis_test Is this a test run.
6181 - *
6182 - * @since 3.2
6183 - */
6184 - public function implode_frontend_css( $travis_test = false ) {
6185 - $do_implode = true;
6186 - if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6187 - $do_implode = false;
6188 - }
6189 -
6190 - // Do not implode CSS when the page loads via the AMP plugin.
6191 - if ( Jetpack_AMP_Support::is_amp_request() ) {
6192 - $do_implode = false;
6193 - }
6194 -
6195 - /**
6196 - * Allow CSS to be concatenated into a single jetpack.css file.
6197 - *
6198 - * @since 3.2.0
6199 - *
6200 - * @param bool $do_implode Should CSS be concatenated? Default to true.
6201 - */
6202 - $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6203 -
6204 - // Do not use the imploded file when default behavior was altered through the filter.
6205 - if ( ! $do_implode ) {
6206 - return;
6207 - }
6208 -
6209 - // We do not want to use the imploded file in dev mode, or if not connected.
6210 - if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6211 - if ( ! $travis_test ) {
6212 - return;
6213 - }
6214 - }
6215 -
6216 - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen.
6217 - if ( get_option( 'sharedaddy_disable_resources' ) ) {
6218 - return;
6219 - }
6220 -
6221 - /*
6222 - * Now we assume Jetpack is connected and able to serve the single
6223 - * file.
6224 - *
6225 - * In the future there will be a check here to serve the file locally
6226 - * or potentially from the Jetpack CDN
6227 - *
6228 - * For now:
6229 - * - Enqueue a single imploded css file
6230 - * - Zero out the style_loader_tag for the bundled ones
6231 - * - Be happy, drink scotch
6232 - */
6233 -
6234 - add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6235 -
6236 - $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6237 -
6238 - wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6239 - wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6240 - }
6241 -
6242 - /**
6243 - * Removes styles that are part of concatenated group.
6244 - *
6245 - * @param string $tag Style tag.
6246 - * @param string $handle Style handle.
6247 - *
6248 - * @return string
6249 - */
6250 - public function concat_remove_style_loader_tag( $tag, $handle ) {
6251 - if ( in_array( $handle, $this->concatenated_style_handles, true ) ) {
6252 - $tag = '';
6253 - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6254 - $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6255 - }
6256 - }
6257 -
6258 - return $tag;
6259 - }
6260 -
6261 - /**
6262 6062 * Check the heartbeat data
6263 6063 *
6264 6064 * Organizes the heartbeat data by severity. For example, if the site
6265 6065 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
@@ -6271,9 +6071,23 @@
6271 6071 *
6272 6072 * $return array $filtered_data
6273 6073 */
6274 6074 public static function jetpack_check_heartbeat_data() {
6275 - $raw_data = Jetpack_Heartbeat::generate_stats_array();
6075 + /*
6076 + * Site environment stats (incl. wp-version/php-version checked below) now live in the Connection package,
6077 + * and the IDC stat is contributed by the Connection package's `jetpack_heartbeat_stats_array` filter
6078 + * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6079 + * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6080 + * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6081 + */
6082 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
6083 + ? Heartbeat::get_environment_stats()
6084 + : array();
6085 + $raw_data = array_merge(
6086 + Jetpack_Heartbeat::generate_stats_array(),
6087 + $env_stats,
6088 + array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6089 + );
6276 6090
6277 6091 $good = array();
6278 6092 $caution = array();
6279 6093 $bad = array();
@@ -6339,23 +6153,8 @@
6339 6153 return Jetpack_Options::get_options_for_reset();
6340 6154 }
6341 6155
6342 6156 /**
6343 - * Strip http:// or https:// from a url, replaces forward slash with ::,
6344 - * so we can bring them directly to their site in calypso.
6345 - *
6346 - * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6347 - *
6348 - * @param string $url URL.
6349 - * @return string url without the guff.
6350 - */
6351 - public static function build_raw_urls( $url ) {
6352 - _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6353 -
6354 - return ( new Status() )->get_site_suffix( $url );
6355 - }
6356 -
6357 - /**
6358 6157 * Get the user's meta box order.
6359 6158 *
6360 6159 * @param array $sorted Value for the user's option.
6361 6160 * @return mixed
@@ -6365,9 +6164,9 @@
6365 6164 return $sorted;
6366 6165 }
6367 6166
6368 6167 foreach ( $sorted as $box_context => $ids ) {
6369 - if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6168 + if ( ! str_contains( $ids, 'dashboard_stats' ) ) {
6370 6169 // If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6371 6170 continue;
6372 6171 }
6373 6172
@@ -6384,68 +6183,9 @@
6384 6183 }
6385 6184
6386 6185 return $sorted;
6387 6186 }
6388 -
6389 6187 /**
6390 - * Adds a "blank" column in the user admin table to display indication of user connection.
6391 - *
6392 - * @param array $columns User list table columns.
6393 - *
6394 - * @return array
6395 - */
6396 - public function jetpack_icon_user_connected( $columns ) {
6397 - $columns['user_jetpack'] = '';
6398 - return $columns;
6399 - }
6400 -
6401 - /**
6402 - * Show Jetpack icon if the user is linked.
6403 - *
6404 - * @param string $val HTML for the icon.
6405 - * @param string $col User list table column.
6406 - * @param int $user_id User ID.
6407 - *
6408 - * @return string
6409 - */
6410 - public function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
6411 - if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) {
6412 - $jetpack_logo = new Jetpack_Logo();
6413 - $emblem_html = sprintf(
6414 - '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
6415 - esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
6416 - $jetpack_logo->get_jp_emblem()
6417 - );
6418 - return $emblem_html;
6419 - }
6420 -
6421 - return $val;
6422 - }
6423 -
6424 - /**
6425 - * Style the Jetpack user column
6426 - */
6427 - public function jetpack_user_col_style() {
6428 - global $current_screen;
6429 - if ( ! empty( $current_screen->base ) && 'users' === $current_screen->base ) {
6430 - ?>
6431 - <style>
6432 - .fixed .column-user_jetpack {
6433 - width: 21px;
6434 - }
6435 - .jp-emblem-user-admin svg {
6436 - width: 20px;
6437 - height: 20px;
6438 - }
6439 - .jp-emblem-user-admin path {
6440 - fill: #069e08;
6441 - }
6442 - </style>
6443 - <?php
6444 - }
6445 - }
6446 -
6447 - /**
6448 6188 * Checks if Akismet is active and working.
6449 6189 *
6450 6190 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
6451 6191 * that implied usage of methods present since more recent version.
@@ -6537,26 +6277,15 @@
6537 6277 /**
6538 6278 * Return Calypso environment value; used for developing Jetpack and pairing
6539 6279 * it with different Calypso enrionments, such as localhost.
6540 6280 *
6541 - * @since 7.4.0
6281 + * @deprecated 12.4 Moved to the Status package.
6542 6282 *
6543 6283 * @return string Calypso environment
6544 6284 */
6545 6285 public static function get_calypso_env() {
6546 - if ( isset( $_GET['calypso_env'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6547 - return sanitize_key( $_GET['calypso_env'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments.
6548 - }
6549 -
6550 - if ( getenv( 'CALYPSO_ENV' ) ) {
6551 - return sanitize_key( getenv( 'CALYPSO_ENV' ) );
6552 - }
6553 -
6554 - if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
6555 - return sanitize_key( CALYPSO_ENV );
6556 - }
6557 -
6558 - return '';
6286 + _deprecated_function( __METHOD__, 'jetpack-12.4', '\Automattic\Jetpack\Status\Host->get_calypso_env' );
6287 + return ( new Host() )->get_calypso_env();
6559 6288 }
6560 6289
6561 6290 /**
6562 6291 * Returns the hostname with protocol for Calypso.
@@ -6566,9 +6295,9 @@
6566 6295 *
6567 6296 * @return string Calypso host.
6568 6297 */
6569 6298 public static function get_calypso_host() {
6570 - $calypso_env = self::get_calypso_env();
6299 + $calypso_env = ( new Host() )->get_calypso_env();
6571 6300 switch ( $calypso_env ) {
6572 6301 case 'development':
6573 6302 return 'http://calypso.localhost:3000/';
6574 6303 case 'wpcalypso':
@@ -6617,13 +6346,20 @@
6617 6346 }
6618 6347 }
6619 6348
6620 6349 /**
6621 - * Returns a boolean for whether backups UI should be displayed or not.
6350 + * Whether UI for backups should be displayed.
6622 6351 *
6352 + * On WPCom platforms this is gated on the backups-self-serve site feature.
6353 + * On self-hosted Jetpack sites it falls back to the jetpack_show_backups filter.
6354 + *
6623 6355 * @return bool Should backups UI be displayed?
6624 6356 */
6625 6357 public static function show_backups_ui() {
6358 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6359 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'backups-self-serve' );
6360 + }
6361 +
6626 6362 /**
6627 6363 * Whether UI for backups should be displayed.
6628 6364 *
6629 6365 * @since 6.5.0
@@ -6633,8 +6369,24 @@
6633 6369 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
6634 6370 }
6635 6371
6636 6372 /**
6373 + * Whether UI for security scanning should be displayed.
6374 + *
6375 + * On WPCom platforms this is gated on the scan-self-serve site feature.
6376 + * On self-hosted Jetpack sites it always returns true.
6377 + *
6378 + * @return bool Should scan UI be displayed?
6379 + */
6380 + public static function show_scan_ui() {
6381 + if ( ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_platform() ) {
6382 + return function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'scan-self-serve' );
6383 + }
6384 +
6385 + return true;
6386 + }
6387 +
6388 + /**
6637 6389 * Clean leftoveruser meta.
6638 6390 *
6639 6391 * Delete Jetpack-related user meta when it is no longer needed.
6640 6392 *
@@ -6706,8 +6458,40 @@
6706 6458 'url' => Redirect::get_url( 'jetpack-faq-backup-disclaimer' ),
6707 6459 ),
6708 6460 );
6709 6461
6462 + $products['creator'] = array(
6463 + 'title' => __( 'Jetpack Creator', 'jetpack' ),
6464 + 'slug' => 'jetpack_creator_yearly',
6465 + 'description' => __( 'Craft stunning content, boost your subscriber base, and monetize your online presence.', 'jetpack' ),
6466 + 'show_promotion' => true,
6467 + 'discount_percent' => 50,
6468 + 'included_in_plans' => array( 'complete' ),
6469 + 'features' => array(
6470 + _x( 'Unlimited subscriber imports', 'Creator Product Feature', 'jetpack' ),
6471 + _x( 'Earn more from your content', 'Creator Product Feature', 'jetpack' ),
6472 + _x( 'Accept payments with PayPal', 'Creator Product Feature', 'jetpack' ),
6473 + _x( 'Increase earnings with WordAds', 'Creator Product Feature', 'jetpack' ),
6474 + ),
6475 + );
6476 +
6477 + $products['growth'] = array(
6478 + 'title' => __( 'Jetpack Growth', 'jetpack' ),
6479 + 'slug' => 'jetpack_growth_yearly',
6480 + 'description' => __( 'Essential tools to help you grow your audience, track visitor engagement, and turn leads into loyal customers and advocates.', 'jetpack' ),
6481 + 'show_promotion' => true,
6482 + 'discount_percent' => 50,
6483 + 'included_in_plans' => array( 'complete' ),
6484 + 'features' => array(
6485 + _x( 'Jetpack Social', 'Growth Product Feature', 'jetpack' ),
6486 + _x( 'Jetpack Stats (10K site views, upgradeable)', 'Growth Product Feature', 'jetpack' ),
6487 + _x( 'Unlimited subscriber imports', 'Growth Product Feature', 'jetpack' ),
6488 + _x( 'Earn more from your content', 'Growth Product Feature', 'jetpack' ),
6489 + _x( 'Accept payments with PayPal', 'Growth Product Feature', 'jetpack' ),
6490 + _x( 'Increase earnings with WordAds', 'Growth Product Feature', 'jetpack' ),
6491 + ),
6492 + );
6493 +
6710 6494 $products['scan'] = array(
6711 6495 'title' => __( 'Jetpack Scan', 'jetpack' ),
6712 6496 'slug' => 'jetpack_scan',
6713 6497 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ),
@@ -6737,9 +6521,9 @@
6737 6521 ),
6738 6522 );
6739 6523
6740 6524 $products['akismet'] = array(
6741 - 'title' => __( 'Jetpack Anti-Spam', 'jetpack' ),
6525 + 'title' => __( 'Akismet Anti-spam', 'jetpack' ),
6742 6526 'slug' => 'jetpack_anti_spam',
6743 6527 'description' => __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack' ),
6744 6528 'show_promotion' => true,
6745 6529 'discount_percent' => 50,
@@ -6745,9 +6529,8 @@
6745 6529 'discount_percent' => 50,
6746 6530 'included_in_plans' => array( 'security' ),
6747 6531 'features' => array(
6748 6532 _x( 'Comment and form spam protection', 'Anti-Spam Product Feature', 'jetpack' ),
6749 - _x( 'Powered by Akismet', 'Anti-Spam Product Feature', 'jetpack' ),
6750 6533 _x( 'Block spam without CAPTCHAs', 'Anti-Spam Product Feature', 'jetpack' ),
6751 6534 _x( 'Advanced stats', 'Anti-Spam Product Feature', 'jetpack' ),
6752 6535 ),
6753 6536 );
@@ -6834,5 +6617,106 @@
6834 6617
6835 6618 return true;
6836 6619 }
6837 6620
6621 + /**
6622 + * Add 5-star review link on the Jetpack Plugin meta, in the plugins list table (on the plugins page).
6623 + *
6624 + * @param array $plugin_meta An array of the plugin's metadata.
6625 + * @param string $plugin_file Path to the plugin file.
6626 + *
6627 + * @return array $plugin_meta An array of the plugin's metadata.
6628 + */
6629 + public function add_5_star_review_link( $plugin_meta, $plugin_file ) {
6630 + if ( $plugin_file !== 'jetpack/jetpack.php' ) {
6631 + return $plugin_meta;
6632 + }
6633 +
6634 + $u = get_current_user_id();
6635 + $site = get_site_url();
6636 +
6637 + $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">'
6638 + . str_repeat( '<span class="dashicons dashicons-star-filled" style="font-size: 16px; width:16px; height: 16px"></span>', 5 )
6639 + . '</a>';
6640 +
6641 + return $plugin_meta;
6642 + }
6643 +
6644 + /**
6645 + * Lazy instantiation of the Plugin_Tracking object.
6646 + *
6647 + * @since 13.9
6648 + *
6649 + * @return void
6650 + */
6651 + public function initialize_tracking() {
6652 + if ( did_action( 'jetpack_initialize_tracking' ) > 1 ) {
6653 + // Only need to run once.
6654 + return;
6655 + }
6656 +
6657 + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) || static::is_connection_ready() ) {
6658 + ( new Plugin_Tracking() )->init();
6659 + }
6660 + }
6661 +
6662 + /**
6663 + * Run the "initialize tracking" hook.
6664 + *
6665 + * @since 13.9
6666 + */
6667 + public function run_initialize_tracking_action() {
6668 + /**
6669 + * Fires when the tracking needs to be initialized.
6670 + * Doesn't necessarily mean that will actually happen, depends if the 'jetpack_tos_agreed' option is set.
6671 + *
6672 + * @since 13.9
6673 + */
6674 + do_action( 'jetpack_initialize_tracking' );
6675 + }
6676 +
6677 + /**
6678 + * Initialize REST jsonAPI if needed.
6679 + *
6680 + * @return void
6681 + */
6682 + public function maybe_initialize_rest_jsonapi() {
6683 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
6684 + if ( ! empty( $_GET['jsonapi'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) {
6685 + require_once ABSPATH . 'wp-admin/includes/admin.php'; // JSON API relies on WP functionality not autoloaded in REST.
6686 +
6687 + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
6688 + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
6689 + }
6690 + }
6691 +
6692 + /**
6693 + * Run plugin post-activation actions if we need to.
6694 + *
6695 + * @return void
6696 + */
6697 + private function plugin_post_activation() {
6698 + if ( ( new Status() )->is_offline_mode() ) {
6699 + return;
6700 + }
6701 +
6702 + if ( get_transient( 'activated_jetpack' ) ) {
6703 + delete_transient( 'activated_jetpack' );
6704 +
6705 + if ( ( new Host() )->is_woa_site() ) {
6706 + $redirect_url = static::admin_url( 'page=jetpack' );
6707 + } elseif ( is_network_admin() ) {
6708 + $redirect_url = admin_url( 'network/admin.php?page=jetpack' );
6709 + } elseif ( get_transient( 'my_jetpack_product_activated' ) ) {
6710 + // don't redirect if this is an activation that just came from My Jetpack
6711 + // My Jetpack has its own set of post-activation redirects
6712 + return;
6713 + } elseif ( My_Jetpack_Initializer::should_initialize() ) {
6714 + $redirect_url = static::admin_url( 'page=my-jetpack' );
6715 + } else {
6716 + $redirect_url = static::admin_url( 'page=jetpack' );
6717 + }
6718 +
6719 + wp_safe_redirect( $redirect_url );
6720 + }
6721 + }
6838 6722 }