PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
← All changes | class.jetpack.php +99 -218 16.2 → 16.3-a.7 View file →
@@ -6,9 +6,8 @@
6 6 *
7 7 * @package automattic/jetpack
8 8 */
9 9
10 -use Automattic\Jetpack\Activity_Log\Jetpack_Activity_Log as Activity_Log_Init;
11 10 use Automattic\Jetpack\Assets;
12 11 use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
13 12 use Automattic\Jetpack\Config;
14 13 use Automattic\Jetpack\Connection\Authorize_Json_Api;
@@ -22,8 +21,9 @@
22 21 use Automattic\Jetpack\CookieState;
23 22 use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
24 23 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
25 24 use Automattic\Jetpack\Errors;
25 +use Automattic\Jetpack\Feature_Policy;
26 26 use Automattic\Jetpack\Files;
27 27 use Automattic\Jetpack\Heartbeat;
28 28 use Automattic\Jetpack\Identity_Crisis;
29 29 use Automattic\Jetpack\Import\Main as Import_Main;
@@ -80,20 +80,8 @@
80 80 * The Jetpack class.
81 81 */
82 82 class Jetpack {
83 83 /**
84 - * Marks that the durable Jetpack SEO module-state options have been reconciled against
85 - * the site's module configuration, so the repair runs at most once.
86 - *
87 - * {@see self::reconcile_seo_module_state_options()}
88 - *
89 - * @since 16.1
90 - *
91 - * @var string
92 - */
93 - const SEO_MODULE_STATE_RECONCILED_OPTION = 'jetpack_seo_module_state_reconciled';
94 -
95 - /**
96 84 * XMLRPC server instance.
97 85 *
98 86 * @var null|Jetpack_XMLRPC_Server XMLRPC server used by Jetpack.
99 87 */
@@ -556,8 +544,11 @@
556 544 if ( false === get_option( 'wpcom_newsletter_send_default' ) ) {
557 545 add_option( 'wpcom_newsletter_send_default', 1 );
558 546 }
559 547
548 + // Its handler went with the Recommendations assistant.
549 + wp_clear_scheduled_hook( 'jetpack_recommend_videopress' );
550 +
560 551 if ( did_action( 'wp_loaded' ) ) {
561 552 self::upgrade_on_load();
562 553 } else {
563 554 add_action(
@@ -785,11 +776,8 @@
785 776
786 777 // Register product descriptions for partner coupon usage.
787 778 add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) );
788 779
789 - // Actions for conditional recommendations.
790 - add_action( 'plugins_loaded', array( 'Jetpack_Recommendations', 'init_conditional_recommendation_actions' ) );
791 -
792 780 // Add 5-star
793 781 add_filter( 'plugin_row_meta', array( $this, 'add_5_star_review_link' ), 10, 2 );
794 782 add_action( 'init', array( Deprecate::class, 'instance' ) );
795 783
@@ -851,8 +839,46 @@
851 839 }
852 840 }
853 841
854 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 + /**
857 + * Filters whether the Jetpack plugin offers its bundled Backup dashboard.
858 + *
859 + * Resolved at the earliest `plugins_loaded` priority, so hook it from a mu-plugin.
860 + *
861 + * @since 16.3
862 + *
863 + * @param bool $enabled Whether to initialize the bundled Backup dashboard. Default true.
864 + */
865 + if ( ! apply_filters( 'jetpack_backup_dashboard_enabled', true ) ) {
866 + return;
867 + }
868 +
869 + $backup = 'Automattic\\Jetpack\\Backup\\V0005\\Jetpack_Backup';
870 +
871 + // An older package would take over the connection (see Jetpack_Backup::DEFAULT_INIT_OPTIONS);
872 + // only the standalone plugin ships one that old, and it draws its own menu.
873 + if ( ! class_exists( $backup ) || ! defined( $backup . '::DEFAULT_INIT_OPTIONS' ) ) {
874 + return;
875 + }
876 +
877 + $backup::initialize( array( 'manage_connection' => false ) );
878 + }
879 +
880 + /**
855 881 * Whether the bundled Stats v2 dashboard is enabled.
856 882 *
857 883 * Stats v2 (formerly "Premium Analytics") ships with the plugin behind this
858 884 * flag while it rolls out (WOOA7S-1595). When enabled it adds its own admin
@@ -946,8 +972,15 @@
946 972 // only renders when the VideoPress module is active (Status::is_active()); when it
947 973 // is not, the menu item links to the My Jetpack interstitial to activate it.
948 974 $config->ensure( 'videopress', array( 'admin_ui' => true ) );
949 975
976 + // The Backup dashboard is only an admin menu and REST routes, so it is deferred like Import below.
977 + if ( self::should_eager_load_packages() ) {
978 + self::configure_backup_package();
979 + } else {
980 + add_action( 'rest_api_init', array( __CLASS__, 'configure_backup_package' ), 0 );
981 + }
982 +
950 983 /*
951 984 * The Import package only registers `jetpack/v4/import` REST routes — it
952 985 * does nothing when rendering a front-end page — so gate its `ensure()`
953 986 * to keep its PHP out of opcache on the front-end GET hot path. It still
@@ -1143,9 +1176,8 @@
1143 1176 } else {
1144 1177 add_action( 'rest_api_init', array( My_Jetpack_Initializer::class, 'init' ), 0 );
1145 1178 }
1146 1179
1147 - Activity_Log_Init::initialize();
1148 1180 Scan_Page_Init::initialize();
1149 1181 Jetpack_SEO_Initializer::init();
1150 1182
1151 1183 if ( ( new Modules() )->is_active( 'podcast' ) ) {
@@ -1287,8 +1319,9 @@
1287 1319 $jetpack_callables = array(
1288 1320 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1289 1321 'updates' => array( 'Jetpack', 'get_updates' ),
1290 1322 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1323 + 'theme_styles' => array( 'Automattic\\Jetpack\\Plugin\\Theme_Styles_Sync', 'get_theme_styles' ),
1291 1324 );
1292 1325 return array_merge( $callables, $jetpack_callables );
1293 1326 }
1294 1327
@@ -2043,9 +2076,13 @@
2043 2076 *
2044 2077 * @todo Store the result in core's object cache maybe?
2045 2078 */
2046 2079 public static function get_active_plugins() {
2047 - // Delegates to the canonical implementation in the Connection package.
2080 + // Older Connection copies can load first and lack this method.
2081 + if ( ! method_exists( Heartbeat::class, 'get_active_plugins' ) ) {
2082 + return array();
2083 + }
2084 +
2048 2085 return Heartbeat::get_active_plugins();
2049 2086 }
2050 2087
2051 2088 /**
@@ -2339,8 +2376,12 @@
2339 2376 default:
2340 2377 break;
2341 2378 }
2342 2379 }
2380 + if ( method_exists( Feature_Policy::class, 'ensure_hooks' ) ) {
2381 + Feature_Policy::ensure_hooks();
2382 + }
2383 +
2343 2384 /**
2344 2385 * Filters the array of default modules.
2345 2386 *
2346 2387 * @since 2.5.0
@@ -2500,23 +2541,30 @@
2500 2541
2501 2542 /**
2502 2543 * Return module name translation. Uses matching string created in modules/module-headings.php.
2503 2544 *
2545 + * The module list is globbed from `modules/` at runtime, so a module can be listed with no
2546 + * entry in that generated file. Fall back to the untranslated header rather than overwriting
2547 + * it with the null `jetpack_get_module_i18n()` returns for an unknown slug.
2548 + *
2504 2549 * @since 3.9.2
2505 2550 *
2506 2551 * @param array $modules Array of Jetpack modules.
2507 2552 *
2508 - * @return string|void
2553 + * @return array
2509 2554 */
2510 2555 public static function get_translated_modules( $modules ) {
2511 2556 foreach ( $modules as $index => $module ) {
2512 2557 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2513 - if ( isset( $module['name'] ) ) {
2514 - $modules[ $index ]['name'] = $i18n_module['name'];
2558 + $name = $i18n_module['name'] ?? null;
2559 + $description = $i18n_module['description'] ?? null;
2560 +
2561 + if ( null !== $name && isset( $module['name'] ) ) {
2562 + $modules[ $index ]['name'] = $name;
2515 2563 }
2516 - if ( isset( $module['description'] ) ) {
2517 - $modules[ $index ]['description'] = $i18n_module['description'];
2518 - $modules[ $index ]['short_description'] = $i18n_module['description'];
2564 + if ( null !== $description && isset( $module['description'] ) ) {
2565 + $modules[ $index ]['description'] = $description;
2566 + $modules[ $index ]['short_description'] = $description;
2519 2567 }
2520 2568 if ( isset( $module['module_tags'] ) ) {
2521 2569 $modules[ $index ]['module_tags'] = array_map( 'jetpack_get_module_i18n_tag', $module['module_tags'] );
2522 2570 }
@@ -2829,9 +2877,9 @@
2829 2877 * @return string $url module configuration URL.
2830 2878 */
2831 2879 public static function module_configuration_url( $module ) {
2832 2880 $module = self::get_module_slug( $module );
2833 - $default_url = self::admin_url() . "#/settings?term=$module";
2881 + $default_url = self::admin_url( array( 'page' => 'jetpack-settings' ) ) . "#/settings?term=$module";
2834 2882 /**
2835 2883 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
2836 2884 *
2837 2885 * @since 6.9.0
@@ -3107,199 +3155,19 @@
3107 3155 update_option( self::AI_MASTER_OPTOUT_MIGRATED_OPTION, true );
3108 3156 }
3109 3157
3110 3158 /**
3111 - * Whether a module should be recorded as active in its durable Jetpack SEO option.
3159 + * Deletes obsolete SEO module-state options without changing module activation.
3112 3160 *
3113 - * Permanent module overrides are part of the site's configuration, so the normal
3114 - * filtered module state is authoritative. The exception is wpcomsh's private-site
3115 - * callback, which temporarily suppresses `sitemaps` on Atomic sites without changing
3116 - * the site's configured state. Evaluate the module state with only that callback
3117 - * disabled.
3118 - *
3119 - * The hook is cloned before removing the callback so its ordering remains unchanged for
3120 - * the rest of the request. `$available_only` is false because these migrations can run
3121 - * after the standalone module file has been removed.
3122 - *
3123 - * WordPress.com Simple keeps module state outside this site's options table, so its
3124 - * normal filtered read remains authoritative.
3125 - *
3126 - * @since 16.1
3127 - *
3128 - * @param string $module Module slug.
3129 - * @return bool Whether the module should be recorded as active.
3161 + * @since 16.3
3130 3162 */
3131 - private static function is_module_active_for_seo_option( $module ) {
3132 - $modules = new Modules();
3133 -
3134 - if ( ( new Host() )->is_wpcom_simple() ) {
3135 - return $modules->is_active( $module, false );
3136 - }
3137 -
3138 - global $wp_filter;
3139 -
3140 - $hook_name = 'jetpack_active_modules';
3141 - $private_site_callback = '\Private_Site\filter_jetpack_active_modules';
3142 - $callback_priority = has_filter( $hook_name, $private_site_callback );
3143 -
3144 - if ( false === $callback_priority ) {
3145 - return $modules->is_active( $module, false );
3146 - }
3147 -
3148 - $original_hook = $wp_filter[ $hook_name ];
3149 - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Use an isolated hook copy without changing callback order.
3150 - $wp_filter[ $hook_name ] = clone $original_hook;
3151 - remove_filter( $hook_name, $private_site_callback, $callback_priority );
3152 -
3153 - try {
3154 - return $modules->is_active( $module, false );
3155 - } finally {
3156 - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore the untouched original hook.
3157 - $wp_filter[ $hook_name ] = $original_hook;
3158 - }
3163 + public static function cleanup_seo_module_state_options() {
3164 + delete_option( 'jetpack_seo_sitemap_enabled' );
3165 + delete_option( 'jetpack_seo_canonical_urls_enabled' );
3166 + delete_option( 'jetpack_seo_module_state_reconciled' );
3159 3167 }
3160 3168
3161 3169 /**
3162 - * Records whether the standalone Sitemaps module is active so the setting survives
3163 - * the module's removal.
3164 - *
3165 - * The Jetpack SEO product reads the {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION}
3166 - * option instead of the `sitemaps` module's active state. Module-active state is
3167 - * filtered against the modules present on disk, so once the standalone module is
3168 - * removed it would read as inactive even for sites that had it on. This one-time
3169 - * migration captures the configured module state — which persists regardless of
3170 - * whether the module file is present — into the durable option.
3171 - *
3172 - * Deliberately non-destructive: it never touches generated sitemap data
3173 - * (`jp_sitemap*` posts), the `jetpack-sitemap-state` option, sitemap settings, or the
3174 - * `jp_sitemap_cron_hook` cron, so no regeneration is triggered. `add_option()` only
3175 - * seeds when the option is absent, so it is safe to run on every version bump and
3176 - * never reverts a value the user has since set.
3177 - *
3178 - * Hooked on `updating_jetpack_version`; the version arguments are not needed because
3179 - * `add_option()` provides the run-once guard.
3180 - */
3181 - public static function migrate_sitemaps_module_to_seo_option() {
3182 - // Ignore wpcomsh's temporary private-site suppression while preserving permanent
3183 - // module overrides. {@see self::is_module_active_for_seo_option()}.
3184 - $sitemaps_active = self::is_module_active_for_seo_option( 'sitemaps' );
3185 -
3186 - add_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, $sitemaps_active );
3187 - }
3188 -
3189 - /**
3190 - * Keeps the Jetpack SEO sitemap option in sync with the legacy `sitemaps` module
3191 - * while both still exist.
3192 - *
3193 - * Hooked to the module's activate/deactivate actions, so toggling sitemaps from any
3194 - * surface (the legacy Traffic settings, the SEO Settings tab, or WP-CLI) keeps the
3195 - * durable {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} option current. The
3196 - * actions fire after `active_modules` is updated, so the configured state already
3197 - * reflects the new choice. Ignore temporary private-site suppression without bypassing
3198 - * permanent module overrides. Removed alongside the module itself.
3199 - */
3200 - public static function sync_seo_sitemap_option() {
3201 - update_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, self::is_module_active_for_seo_option( 'sitemaps' ) );
3202 - }
3203 -
3204 - /**
3205 - * Records whether the standalone Canonical URLs module is active so the setting survives
3206 - * the module's removal.
3207 - *
3208 - * The Jetpack SEO product reads the {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION}
3209 - * option instead of the `canonical-urls` module's active state. Module-active state is
3210 - * filtered against the modules present on disk, so once the standalone module is
3211 - * removed it would read as inactive even for sites that had it on. This one-time
3212 - * migration captures the configured module state — which persists regardless of
3213 - * whether the module file is present — into the durable option.
3214 - *
3215 - * Deliberately non-destructive: `add_option()` only seeds when the option is absent, so
3216 - * it is safe to run on every version bump and never reverts a value the user has since
3217 - * set.
3218 - *
3219 - * Hooked on `updating_jetpack_version`; the version arguments are not needed because
3220 - * `add_option()` provides the run-once guard.
3221 - */
3222 - public static function migrate_canonical_urls_module_to_seo_option() {
3223 - $canonical_active = self::is_module_active_for_seo_option( 'canonical-urls' );
3224 -
3225 - add_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, $canonical_active );
3226 - }
3227 -
3228 - /**
3229 - * Keeps the Jetpack SEO canonical-urls option in sync with the legacy `canonical-urls`
3230 - * module while both still exist.
3231 - *
3232 - * Hooked to the module's activate/deactivate actions, so toggling canonical URLs from any
3233 - * surface (the legacy Traffic settings, the SEO Settings tab, or WP-CLI) keeps the
3234 - * durable {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION} option current. The
3235 - * actions fire after `active_modules` is updated, so the configured state already
3236 - * reflects the new choice. Permanent module overrides remain authoritative. Removed
3237 - * alongside the module itself.
3238 - */
3239 - public static function sync_seo_canonical_urls_option() {
3240 - update_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, self::is_module_active_for_seo_option( 'canonical-urls' ) );
3241 - }
3242 -
3243 - /**
3244 - * Repairs durable SEO module-state options that the first pass of the migration seeded
3245 - * from a filtered read.
3246 - *
3247 - * Jetpack 16.0 seeded {@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} and
3248 - * {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION} through
3249 - * {@see Modules::is_active()}, which passes the `jetpack_active_modules` filter. A site
3250 - * that was private at the time therefore recorded `sitemaps` as off even though the user
3251 - * had it on, and because the migration seeds with `add_option()` the value was never
3252 - * revisited — making the site public again did not restore the setting.
3253 - *
3254 - * This runs once and rewrites both options from the site's configured module state,
3255 - * including permanent module overrides. That is safe against a choice the user has made
3256 - * since: every surface that toggles these settings (the legacy Traffic page, the SEO
3257 - * Settings tab, WP-CLI) goes through {@see Modules::activate()}/{@see Modules::deactivate()},
3258 - * so the module state and durable option are already in agreement wherever the original
3259 - * migration got it right.
3260 - *
3261 - * Idempotent: the marker is written with `add_option()`, so reruns on later version bumps
3262 - * are no-ops. Like the migrations it seeds from, it touches no sitemap data or cron state.
3263 - *
3264 - * @since 16.1
3265 - */
3266 - public static function reconcile_seo_module_state_options() {
3267 - if ( get_option( self::SEO_MODULE_STATE_RECONCILED_OPTION ) ) {
3268 - return;
3269 - }
3270 -
3271 - update_option( Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION, self::is_module_active_for_seo_option( 'sitemaps' ) );
3272 - update_option( Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION, self::is_module_active_for_seo_option( 'canonical-urls' ) );
3273 -
3274 - add_option( self::SEO_MODULE_STATE_RECONCILED_OPTION, true );
3275 - }
3276 -
3277 - /**
3278 - * Wires up the migration + sync hooks that keep the durable Jetpack SEO module-state
3279 - * options ({@see Jetpack_SEO_Initializer::SITEMAP_ENABLED_OPTION} /
3280 - * {@see Jetpack_SEO_Initializer::CANONICAL_ENABLED_OPTION}) seeded and in sync with their
3281 - * legacy modules. Called once from `load-jetpack.php`.
3282 - *
3283 - * Extracted from file scope so the wiring is unit-testable (file-scope `add_action()`
3284 - * calls run during bootstrap and can't be exercised by a test). Removed alongside the
3285 - * modules in the deferred post-convergence follow-up that absorbs them into Jetpack SEO.
3286 - */
3287 - public static function register_seo_module_migration_hooks() {
3288 - add_action( 'updating_jetpack_version', array( 'Jetpack', 'migrate_sitemaps_module_to_seo_option' ) );
3289 - add_action( 'jetpack_activate_module_sitemaps', array( 'Jetpack', 'sync_seo_sitemap_option' ) );
3290 - add_action( 'jetpack_deactivate_module_sitemaps', array( 'Jetpack', 'sync_seo_sitemap_option' ) );
3291 -
3292 - add_action( 'updating_jetpack_version', array( 'Jetpack', 'migrate_canonical_urls_module_to_seo_option' ) );
3293 - add_action( 'jetpack_activate_module_canonical-urls', array( 'Jetpack', 'sync_seo_canonical_urls_option' ) );
3294 - add_action( 'jetpack_deactivate_module_canonical-urls', array( 'Jetpack', 'sync_seo_canonical_urls_option' ) );
3295 -
3296 - // Runs after both migrations above (default priority, registered last) so a freshly
3297 - // seeded site is already correct and the reconciliation is a no-op there.
3298 - add_action( 'updating_jetpack_version', array( 'Jetpack', 'reconcile_seo_module_state_options' ) );
3299 - }
3300 -
3301 - /**
3302 3170 * Seeds the Jetpack SEO discoverability cohort once, so the new SEO surface is
3303 3171 * auto-discoverable on fresh installs but opt-in on existing ones (JETPACK-1700).
3304 3172 *
3305 3173 * Hooked on `updating_jetpack_version`, which fires on every install including the
@@ -3665,10 +3533,12 @@
3665 3533 */
3666 3534 public static function get_stat_data( $encode = true, $extended = true ) {
3667 3535 _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\\Jetpack\\Heartbeat::generate_stats_array' );
3668 3536
3669 - // Site environment stats now live in the Connection package; merge them with the Jetpack-specific stats.
3670 - $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), Heartbeat::get_environment_stats() );
3537 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
3538 + ? Heartbeat::get_environment_stats()
3539 + : array();
3540 + $data = array_merge( Jetpack_Heartbeat::generate_stats_array(), $env_stats );
3671 3541
3672 3542 if ( $extended ) {
3673 3543 $additional_data = self::get_additional_stat_data();
3674 3544 $data = array_merge( $data, $additional_data );
@@ -4129,9 +3999,9 @@
4129 3999 */
4130 4000 public function plugin_action_links( $actions ) {
4131 4001 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
4132 4002 return array_merge(
4133 - array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
4003 + array( 'settings' => sprintf( '<a href="%s">%s</a>', esc_url( self::admin_url( 'page=jetpack-settings#/settings' ) ), __( 'Settings', 'jetpack' ) ) ),
4134 4004 $actions
4135 4005 );
4136 4006 }
4137 4007
@@ -4167,9 +4037,9 @@
4167 4037 Assets::enqueue_script( 'jetpack-plugins-page-js' );
4168 4038
4169 4039 // Add objects to be passed to the initial state of the app.
4170 4040 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
4171 - wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_minimal_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
4041 + 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' );
4172 4042
4173 4043 add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) );
4174 4044 }
4175 4045 }
@@ -5211,9 +5081,13 @@
5211 5081 * @return boolean
5212 5082 * @since 2.3.3
5213 5083 */
5214 5084 public static function permit_ssl( $force_recheck = false ) {
5215 - // Delegates to the canonical SSL check in the Connection package.
5085 + if ( ! method_exists( Heartbeat::class, 'permit_ssl' ) ) {
5086 + // Skip the SSL-fail notice when the check cannot run.
5087 + return true;
5088 + }
5089 +
5216 5090 return Heartbeat::permit_ssl( $force_recheck );
5217 5091 }
5218 5092
5219 5093 /**
@@ -5226,8 +5100,12 @@
5226 5100 *
5227 5101 * @return string The localized message, or an empty string when there is no failure.
5228 5102 */
5229 5103 public static function get_ssl_test_message() {
5104 + if ( ! method_exists( Heartbeat::class, 'get_ssl_test_error' ) ) {
5105 + return '';
5106 + }
5107 +
5230 5108 $error = Heartbeat::get_ssl_test_error();
5231 5109
5232 5110 switch ( $error['code'] ) {
5233 5111 case 'no_ssl_support':
@@ -6196,11 +6074,14 @@
6196 6074 * callback. We rebuild the stat here rather than running that filter: the filter's callbacks have side
6197 6075 * effects (Connection\Manager::add_stats_to_heartbeat() consumes and deletes the `xmlrpc_errors` option)
6198 6076 * and return non-scalar values, neither of which is appropriate for this read-only diagnostic.
6199 6077 */
6200 - $raw_data = array_merge(
6078 + $env_stats = method_exists( Heartbeat::class, 'get_environment_stats' )
6079 + ? Heartbeat::get_environment_stats()
6080 + : array();
6081 + $raw_data = array_merge(
6201 6082 Jetpack_Heartbeat::generate_stats_array(),
6202 - Heartbeat::get_environment_stats(),
6083 + $env_stats,
6203 6084 array( 'identitycrisis' => Identity_Crisis::check_identity_crisis() ? 'yes' : 'no' )
6204 6085 );
6205 6086
6206 6087 $good = array();