| @@ -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(); |