diff options
Diffstat (limited to 'plugins/jetpack/class.jetpack.php')
-rw-r--r-- | plugins/jetpack/class.jetpack.php | 4120 |
1 files changed, 2137 insertions, 1983 deletions
diff --git a/plugins/jetpack/class.jetpack.php b/plugins/jetpack/class.jetpack.php index ff8aa073..acd8c33e 100644 --- a/plugins/jetpack/class.jetpack.php +++ b/plugins/jetpack/class.jetpack.php @@ -1,20 +1,38 @@ -<?php +<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName +/** + * The mega-class. + * + * This contains too much, so please think twice before adding more. + * + * @package automattic/jetpack + */ + use Automattic\Jetpack\Assets; use Automattic\Jetpack\Assets\Logo as Jetpack_Logo; use Automattic\Jetpack\Config; use Automattic\Jetpack\Connection\Client; use Automattic\Jetpack\Connection\Manager as Connection_Manager; +use Automattic\Jetpack\Connection\Nonce_Handler; +use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication; +use Automattic\Jetpack\Connection\Secrets; +use Automattic\Jetpack\Connection\Tokens; use Automattic\Jetpack\Connection\Utils as Connection_Utils; +use Automattic\Jetpack\Connection\Webhooks as Connection_Webhooks; use Automattic\Jetpack\Constants; +use Automattic\Jetpack\Device_Detection\User_Agent_Info; +use Automattic\Jetpack\Identity_Crisis; +use Automattic\Jetpack\Licensing; +use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer; use Automattic\Jetpack\Partner; -use Automattic\Jetpack\Roles; +use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking; +use Automattic\Jetpack\Redirect; use Automattic\Jetpack\Status; -use Automattic\Jetpack\Sync\Functions; +use Automattic\Jetpack\Status\Host; +use Automattic\Jetpack\Sync\Actions as Sync_Actions; +use Automattic\Jetpack\Sync\Health; use Automattic\Jetpack\Sync\Sender; -use Automattic\Jetpack\Sync\Users; use Automattic\Jetpack\Terms_Of_Service; use Automattic\Jetpack\Tracking; -use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking; /* Options: @@ -40,19 +58,26 @@ jetpack_do_activate (bool) require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php'; +/** + * The Jetpack class. + */ class Jetpack { + /** + * XMLRPC server instance. + * + * @var null|Jetpack_XMLRPC_Server XMLRPC server used by Jetpack. + */ public $xmlrpc_server = null; - private $rest_authentication_status = null; - - public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA'] - /** - * @var array The handles of styles that are concatenated into jetpack.css. + * The handles of styles that are concatenated into jetpack.css. * * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js. + * + * @var array The handles of styles that are concatenated into jetpack.css. */ public $concatenated_style_handles = array( + 'jetpack-carousel-swiper-css', 'jetpack-carousel', 'grunion.css', 'the-neverending-homepage', @@ -80,24 +105,22 @@ class Jetpack { 'jetpack-search-widget', 'jetpack-simple-payments-widget-style', 'jetpack-widget-social-icons-styles', + 'wpcom_instagram_widget', + 'milestone-widget', ); /** - * The handles of scripts that can be loaded asynchronously. + * Contains all assets that have had their URL rewritten to minified versions. * * @var array */ - public $async_script_handles = array( - 'woocommerce-analytics', - ); + static $min_assets = array(); /** - * Contains all assets that have had their URL rewritten to minified versions. + * Plugins to deactivate. * - * @var array + * @var array Plugins to deactivate by module. */ - static $min_assets = array(); - public $plugins_to_deactivate = array( 'stats' => array( 'stats/stats.php', 'WordPress.com Stats' ), 'shortlinks' => array( 'stats/stats.php', 'WordPress.com Stats' ), @@ -167,9 +190,6 @@ class Jetpack { 'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php', 'Ninja Forms' => 'ninja-forms/ninja-forms.php', ), - 'minileven' => array( - 'WPtouch' => 'wptouch/wptouch.php', - ), 'latex' => array( 'LaTeX for WordPress' => 'latex/latex.php', 'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php', @@ -218,6 +238,8 @@ class Jetpack { 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php', 'All in One SEO Pack Pro' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php', 'The SEO Framework' => 'autodescription/autodescription.php', + 'Rank Math' => 'seo-by-rank-math/rank-math.php', + 'Slim SEO' => 'slim-seo/slim-seo.php', ), 'verification-tools' => array( 'WordPress SEO by Yoast' => 'wordpress-seo/wp-seo.php', @@ -225,6 +247,8 @@ class Jetpack { 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php', 'All in One SEO Pack Pro' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php', 'The SEO Framework' => 'autodescription/autodescription.php', + 'Rank Math' => 'seo-by-rank-math/rank-math.php', + 'Slim SEO' => 'slim-seo/slim-seo.php', ), 'widget-visibility' => array( 'Widget Logic' => 'widget-logic/widget_logic.php', @@ -246,6 +270,8 @@ class Jetpack { 'Simple Sitemap' => 'simple-sitemap/simple-sitemap.php', 'XML Sitemaps' => 'xml-sitemaps/xml-sitemaps.php', 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php', + 'Rank Math' => 'seo-by-rank-math/rank-math.php', + 'Slim SEO' => 'slim-seo/slim-seo.php', ), 'lazy-images' => array( 'Lazy Load' => 'lazy-load/lazy-load.php', @@ -262,74 +288,73 @@ class Jetpack { * * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter: * add_filter( 'jetpack_enable_open_graph', '__return_false' ); + * + * @var array Array of plugin slugs. */ private $open_graph_conflicting_plugins = array( - '2-click-socialmedia-buttons/2-click-socialmedia-buttons.php', - // 2 Click Social Media Buttons - 'add-link-to-facebook/add-link-to-facebook.php', // Add Link to Facebook - 'add-meta-tags/add-meta-tags.php', // Add Meta Tags - 'complete-open-graph/complete-open-graph.php', // Complete Open Graph - 'easy-facebook-share-thumbnails/esft.php', // Easy Facebook Share Thumbnail - 'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php', - // Open Graph Meta Tags by Heateor - 'facebook/facebook.php', // Facebook (official plugin) - 'facebook-awd/AWD_facebook.php', // Facebook AWD All in one - 'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php', - // Facebook Featured Image & OG Meta Tags - 'facebook-meta-tags/facebook-metatags.php', // Facebook Meta Tags - 'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php', - // Facebook Open Graph Meta Tags for WordPress - 'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag - 'facebook-thumb-fixer/_facebook-thumb-fixer.php', // Facebook Thumb Fixer - 'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php', - // Fedmich's Facebook Open Graph Meta - 'network-publisher/networkpub.php', // Network Publisher - 'nextgen-facebook/nextgen-facebook.php', // NextGEN Facebook OG - 'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php', - // NextScripts SNAP - 'og-tags/og-tags.php', // OG Tags - 'opengraph/opengraph.php', // Open Graph - 'open-graph-protocol-framework/open-graph-protocol-framework.php', - // Open Graph Protocol Framework - 'seo-facebook-comments/seofacebook.php', // SEO Facebook Comments - 'seo-ultimate/seo-ultimate.php', // SEO Ultimate - 'sexybookmarks/sexy-bookmarks.php', // Shareaholic - 'shareaholic/sexy-bookmarks.php', // Shareaholic - 'sharepress/sharepress.php', // SharePress - 'simple-facebook-connect/sfc.php', // Simple Facebook Connect - 'social-discussions/social-discussions.php', // Social Discussions - 'social-sharing-toolkit/social_sharing_toolkit.php', // Social Sharing Toolkit - 'socialize/socialize.php', // Socialize - 'squirrly-seo/squirrly.php', // SEO by SQUIRRLY™ - 'only-tweet-like-share-and-google-1/tweet-like-plusone.php', - // Tweet, Like, Google +1 and Share - 'wordbooker/wordbooker.php', // Wordbooker - 'wpsso/wpsso.php', // WordPress Social Sharing Optimization - 'wp-caregiver/wp-caregiver.php', // WP Caregiver - 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', - // WP Facebook Like Send & Open Graph Meta - 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol - 'wp-ogp/wp-ogp.php', // WP-OGP - 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin - 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button - 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox + '2-click-socialmedia-buttons/2-click-socialmedia-buttons.php', // 2 Click Social Media Buttons. + 'add-link-to-facebook/add-link-to-facebook.php', // Add Link to Facebook. + 'add-meta-tags/add-meta-tags.php', // Add Meta Tags. + 'complete-open-graph/complete-open-graph.php', // Complete Open Graph. + 'easy-facebook-share-thumbnails/esft.php', // Easy Facebook Share Thumbnail. + 'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php', // Open Graph Meta Tags by Heateor. + 'facebook/facebook.php', // Facebook (official plugin). + 'facebook-awd/AWD_facebook.php', // Facebook AWD All in one. + 'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php', // Facebook Featured Image & OG Meta Tags. + 'facebook-meta-tags/facebook-metatags.php', // Facebook Meta Tags. + 'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php', // Facebook Open Graph Meta Tags for WordPress. + 'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag. + 'facebook-thumb-fixer/_facebook-thumb-fixer.php', // Facebook Thumb Fixer. + 'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php', // Fedmich's Facebook Open Graph Meta. + 'network-publisher/networkpub.php', // Network Publisher. + 'nextgen-facebook/nextgen-facebook.php', // NextGEN Facebook OG. + 'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php', // NextScripts SNAP. + 'og-tags/og-tags.php', // OG Tags. + 'opengraph/opengraph.php', // Open Graph. + 'open-graph-protocol-framework/open-graph-protocol-framework.php', // Open Graph Protocol Framework. + 'seo-facebook-comments/seofacebook.php', // SEO Facebook Comments. + 'seo-ultimate/seo-ultimate.php', // SEO Ultimate. + 'sexybookmarks/sexy-bookmarks.php', // Shareaholic. + 'shareaholic/sexy-bookmarks.php', // Shareaholic. + 'sharepress/sharepress.php', // SharePress. + 'simple-facebook-connect/sfc.php', // Simple Facebook Connect. + 'social-discussions/social-discussions.php', // Social Discussions. + 'social-sharing-toolkit/social_sharing_toolkit.php', // Social Sharing Toolkit. + 'socialize/socialize.php', // Socialize. + 'squirrly-seo/squirrly.php', // SEO by SQUIRRLY™. + 'only-tweet-like-share-and-google-1/tweet-like-plusone.php', // Tweet, Like, Google +1 and Share. + 'wordbooker/wordbooker.php', // Wordbooker. + 'wpsso/wpsso.php', // WordPress Social Sharing Optimization. + 'wp-caregiver/wp-caregiver.php', // WP Caregiver. + 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php', // WP Facebook Like Send & Open Graph Meta. + 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol. + 'wp-ogp/wp-ogp.php', // WP-OGP. + 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin. + 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button. + 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox. + 'seo-by-rank-math/rank-math.php', // Rank Math. + 'slim-seo/slim-seo.php', // Slim SEO. ); /** * Plugins for which we turn off our Twitter Cards Tags implementation. + * + * @var array Plugins that conflict with Twitter cards. */ private $twitter_cards_conflicting_plugins = array( + // phpcs:ignore Squiz.PHP.CommentedOutCode.Found // 'twitter/twitter.php', // The official one handles this on its own. // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php - 'eewee-twitter-card/index.php', // Eewee Twitter Card - 'ig-twitter-cards/ig-twitter-cards.php', // IG:Twitter Cards - 'jm-twitter-cards/jm-twitter-cards.php', // JM Twitter Cards - 'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php', - // Pure Web Brilliant's Social Graph Twitter Cards Extension - 'twitter-cards/twitter-cards.php', // Twitter Cards - 'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta - 'wp-to-twitter/wp-to-twitter.php', // WP to Twitter - 'wp-twitter-cards/twitter_cards.php', // WP Twitter Cards + 'eewee-twitter-card/index.php', // Eewee Twitter Card. + 'ig-twitter-cards/ig-twitter-cards.php', // IG:Twitter Cards. + 'jm-twitter-cards/jm-twitter-cards.php', // JM Twitter Cards. + 'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php', // Pure Web Brilliant's Social Graph Twitter Cards Extension. + 'twitter-cards/twitter-cards.php', // Twitter Cards. + 'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta. + 'wp-to-twitter/wp-to-twitter.php', // WP to Twitter. + 'wp-twitter-cards/twitter_cards.php', // WP Twitter Cards. + 'seo-by-rank-math/rank-math.php', // Rank Math. + 'slim-seo/slim-seo.php', // Slim SEO. ); /** @@ -362,25 +387,50 @@ class Jetpack { /** * Jetpack_Sync object + * + * @todo This is also seemingly unused. + * + * @var object */ public $sync; /** * Verified data for JSON authorization request + * + * @var array */ public $json_api_authorization_request = array(); /** + * Connection manager. + * * @var Automattic\Jetpack\Connection\Manager */ protected $connection_manager; /** + * Plugin lock key. + * * @var string Transient key used to prevent multiple simultaneous plugin upgrades */ public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock'; /** + * Holds an instance of Automattic\Jetpack\A8c_Mc_Stats + * + * @var Automattic\Jetpack\A8c_Mc_Stats + */ + public $a8c_mc_stats_instance; + + /** + * Constant for login redirect key. + * + * @var string + * @since 8.4.0 + */ + public static $jetpack_redirect_login = 'jetpack_connect_login_redirect'; + + /** * Holds the singleton instance of this class * * @since 2.3.3 @@ -397,6 +447,7 @@ class Jetpack { if ( ! self::$instance ) { self::$instance = new Jetpack(); add_action( 'plugins_loaded', array( self::$instance, 'plugin_upgrade' ) ); + add_action( 'jetpack_idc_disconnect', array( __CLASS__, 'on_idc_disconnect' ) ); } return self::$instance; @@ -406,19 +457,19 @@ class Jetpack { * Must never be called statically */ function plugin_upgrade() { - if ( self::is_active() ) { + if ( self::is_connection_ready() ) { list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) ); if ( JETPACK__VERSION != $version ) { // Prevent multiple upgrades at once - only a single process should trigger - // an upgrade to avoid stampedes + // an upgrade to avoid stampedes. if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) { return; } - // Set a short lock to prevent multiple instances of the upgrade + // Set a short lock to prevent multiple instances of the upgrade. set_transient( self::$plugin_upgrade_lock_key, 1, 10 ); - // check which active modules actually exist and remove others from active_modules list + // check which active modules actually exist and remove others from active_modules list. $unfiltered_modules = self::get_active_modules(); $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) ); if ( array_diff( $unfiltered_modules, $modules ) ) { @@ -427,16 +478,61 @@ class Jetpack { add_action( 'init', array( __CLASS__, 'activate_new_modules' ) ); - // Upgrade to 4.3.0 + // Upgrade to 4.3.0. if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) { Jetpack_Options::delete_option( 'identity_crisis_whitelist' ); } - // Make sure Markdown for posts gets turned back on + // Make sure Markdown for posts gets turned back on. if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) { update_option( 'wpcom_publish_posts_with_markdown', true ); } + /* + * Minileven deprecation. 8.3.0. + * Only delete options if not using + * the replacement standalone Minileven plugin. + */ + if ( + ! self::is_plugin_active( 'minileven-master/minileven.php' ) + && ! self::is_plugin_active( 'minileven/minileven.php' ) + ) { + if ( get_option( 'wp_mobile_custom_css' ) ) { + delete_option( 'wp_mobile_custom_css' ); + } + if ( get_option( 'wp_mobile_excerpt' ) ) { + delete_option( 'wp_mobile_excerpt' ); + } + if ( get_option( 'wp_mobile_featured_images' ) ) { + delete_option( 'wp_mobile_featured_images' ); + } + if ( get_option( 'wp_mobile_app_promos' ) ) { + delete_option( 'wp_mobile_app_promos' ); + } + } + + // Upgrade to 8.4.0. + if ( Jetpack_Options::get_option( 'ab_connect_banner_green_bar' ) ) { + Jetpack_Options::delete_option( 'ab_connect_banner_green_bar' ); + } + + // Update to 8.8.x (WordPress 5.5 Compatibility). + if ( Jetpack_Options::get_option( 'autoupdate_plugins' ) ) { + $updated = update_site_option( + 'auto_update_plugins', + array_unique( + array_merge( + (array) Jetpack_Options::get_option( 'autoupdate_plugins', array() ), + (array) get_site_option( 'auto_update_plugins', array() ) + ) + ) + ); + + if ( $updated ) { + Jetpack_Options::delete_option( 'autoupdate_plugins' ); + } // Should we have some type of fallback if something fails here? + } + if ( did_action( 'wp_loaded' ) ) { self::upgrade_on_load(); } else { @@ -477,7 +573,7 @@ class Jetpack { do_action( 'jetpack_sitemaps_purge_data' ); } - // Delete old stats cache + // Delete old stats cache. delete_option( 'jetpack_restapi_stats_cache' ); delete_transient( self::$plugin_upgrade_lock_key ); @@ -487,7 +583,7 @@ class Jetpack { * Saves all the currently active modules to options. * Also fires Action hooks for each newly activated and deactivated module. * - * @param $modules Array Array of active modules to be saved in options. + * @param array $modules Array of active modules to be saved in options. * * @return $success bool true for success, false for failure. */ @@ -545,11 +641,42 @@ class Jetpack { return $success; } + /** + * Remove all active modules. + * + * @return void + */ static function delete_active_modules() { self::update_active_modules( array() ); } /** + * Adds a hook to plugins_loaded at a priority that's currently the earliest + * available. + */ + public function add_configure_hook() { + global $wp_filter; + + $current_priority = has_filter( 'plugins_loaded', array( $this, 'configure' ) ); + if ( false !== $current_priority ) { + remove_action( 'plugins_loaded', array( $this, 'configure' ), $current_priority ); + } + + $taken_priorities = array_map( 'intval', array_keys( $wp_filter['plugins_loaded']->callbacks ) ); + sort( $taken_priorities ); + + $first_priority = array_shift( $taken_priorities ); + + if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) { + $new_priority = - PHP_INT_MAX; + } else { + $new_priority = $first_priority - 1; + } + + add_action( 'plugins_loaded', array( $this, 'configure' ), $new_priority ); + } + + /** * Constructor. Initializes WordPress hooks */ private function __construct() { @@ -558,18 +685,12 @@ class Jetpack { */ add_action( 'init', array( $this, 'deprecated_hooks' ) ); - add_action( 'plugins_loaded', array( $this, 'configure' ), 1 ); + // Note how this runs at an earlier plugin_loaded hook intentionally to accomodate for other plugins. + add_action( 'plugin_loaded', array( $this, 'add_configure_hook' ), 90 ); + add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 ); + add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 ); add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 ); - add_filter( - 'jetpack_connection_secret_generator', - function( $callable ) { - return function() { - return wp_generate_password( 32, false ); - }; - } - ); - add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) ); add_filter( @@ -585,43 +706,50 @@ class Jetpack { require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php'; add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) ); add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) ); + add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 ); add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) ); add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 ); // Unlink user before deleting the user from WP.com. - add_action( 'deleted_user', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 ); - add_action( 'remove_user_from_blog', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 ); + add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 ); + add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 ); add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 ); - add_filter( 'determine_current_user', array( $this, 'wp_rest_authenticate' ) ); - add_filter( 'rest_authentication_errors', array( $this, 'wp_rest_authentication_errors' ) ); + add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 ); + add_action( 'login_init', array( $this, 'login_init' ) ); + + // Set up the REST authentication hooks. + Connection_Rest_Authentication::init(); add_action( 'admin_init', array( $this, 'admin_init' ) ); add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) ); add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 ); - add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) ); // Filter the dashboard meta box order to swap the new one in in place of the old one. add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) ); - // returns HTTPS support status + // WordPress dashboard widget. + require_once JETPACK__PLUGIN_DIR . 'class-jetpack-stats-dashboard-widget.php'; + add_action( 'wp_dashboard_setup', array( new Jetpack_Stats_Dashboard_Widget(), 'init' ) ); + + // Returns HTTPS support status. add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) ); add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) ); - add_action( 'wp_loaded', array( $this, 'register_assets' ) ); + add_action( 'wp_ajax_jetpack_recommendations_banner', array( 'Jetpack_Recommendations_Banner', 'ajax_callback' ) ); - add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 ); + add_action( 'wp_loaded', array( $this, 'register_assets' ) ); /** * These actions run checks to load additional files. * They check for external files or plugins, so they need to run as late as possible. */ add_action( 'wp_head', array( $this, 'check_open_graph' ), 1 ); - add_action( 'amp_story_head', array( $this, 'check_open_graph' ), 1 ); + add_action( 'web_stories_story_head', array( $this, 'check_open_graph' ), 1 ); add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ), 999 ); add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 ); @@ -634,54 +762,97 @@ class Jetpack { add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) ); add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 ); - // A filter to control all just in time messages - add_filter( 'jetpack_just_in_time_msgs', array( $this, 'is_active_and_not_development_mode' ), 9 ); + require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php'; + $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() ); + add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) ); - add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9 ); + /* + * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin. + * We should make sure to only do this for front end links. + */ + if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) { + add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 ); + add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 ); - // Hide edit post link if mobile app. - if ( Jetpack_User_Agent_Info::is_mobile_app() ) { - add_filter( 'edit_post_link', '__return_empty_string' ); + /* + * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions + * so they point moderation links on emails to Calypso. + */ + jetpack_require_lib( 'functions.wp-notify' ); + add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 ); + add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 ); } - // Update the Jetpack plan from API on heartbeats + add_action( + 'plugins_loaded', + function () { + if ( User_Agent_Info::is_mobile_app() ) { + add_filter( 'get_edit_post_link', '__return_empty_string' ); + } + } + ); + + // Update the site's Jetpack plan and products from API on heartbeats. add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) ); /** * This is the hack to concatenate all css files into one. - * For description and reasoning see the implode_frontend_css method + * For description and reasoning see the implode_frontend_css method. * - * Super late priority so we catch all the registered styles + * Super late priority so we catch all the registered styles. */ if ( ! is_admin() ) { - add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first - add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles` + add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first. + add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`. } - /** - * These are sync actions that we need to keep track of for jitms - */ - add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 ); - // Actually push the stats on shutdown. if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) { add_action( 'shutdown', array( $this, 'push_stats' ) ); } - /* - * Load some scripts asynchronously. - */ - add_action( 'script_loader_tag', array( $this, 'script_add_async' ), 10, 3 ); + // After a successful connection. + add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) ); + add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) ); // Actions for Manager::authorize(). add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) ); add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) ); add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) ); + add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) ); + add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) ); + add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) ); + add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) ); + // Filters for the Manager::get_token() urls and request body. - add_filter( 'jetpack_token_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) ); add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) ); add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) ); + + // Filter for the `jetpack/v4/connection/data` API response. + add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) ); + + // Actions for successful reconnect. + add_action( 'jetpack_reconnection_completed', array( $this, 'reconnection_completed' ) ); + + // Actions for successful disconnect. + add_action( 'jetpack_site_disconnected', array( $this, 'jetpack_site_disconnected' ) ); + + // Actions for licensing. + Licensing::instance()->initialize(); + + // Filters for Sync Callables. + add_filter( 'jetpack_sync_callable_whitelist', array( $this, 'filter_sync_callable_whitelist' ), 10, 1 ); + add_filter( 'jetpack_sync_multisite_callable_whitelist', array( $this, 'filter_sync_multisite_callable_whitelist' ), 10, 1 ); + + // Make resources use static domain when possible. + add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) ); + + // Validate the domain names in Jetpack development versions. + add_action( 'jetpack_pre_register', array( get_called_class(), 'registration_check_domains' ) ); + + // Register product descriptions for partner coupon usage. + add_filter( 'jetpack_partner_coupon_products', array( $this, 'get_partner_coupon_product_descriptions' ) ); } /** @@ -693,21 +864,34 @@ class Jetpack { foreach ( array( - 'connection', 'sync', - 'tracking', - 'tos', + 'jitm', ) as $feature ) { $config->ensure( $feature ); } - if ( is_admin() ) { - $config->ensure( 'jitm' ); - } + $config->ensure( + 'connection', + array( + 'slug' => 'jetpack', + 'name' => 'Jetpack', + ) + ); - $this->connection_manager = new Connection_Manager(); + // Identity crisis package. + $config->ensure( + 'identity_crisis', + array( + 'slug' => 'jetpack', + 'admin_page' => '/wp-admin/admin.php?page=jetpack', + ) + ); + + if ( ! $this->connection_manager ) { + $this->connection_manager = new Connection_Manager( 'jetpack' ); + } /* * Load things that should only be in Network Admin. @@ -721,7 +905,7 @@ class Jetpack { $network->set_connection( $this->connection_manager ); } - if ( $this->connection_manager->is_active() ) { + if ( self::is_connection_ready() ) { add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) ); Jetpack_Heartbeat::init(); @@ -737,28 +921,21 @@ class Jetpack { /* * Enable enhanced handling of previewing sites in Calypso */ - if ( self::is_active() ) { + if ( self::is_connection_ready() ) { require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php'; add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 ); require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php'; add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 ); } - /* - * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin. - * We should make sure to only do this for front end links. - */ - if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) { - add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 ); - add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 ); - - /* - * We'll override wp_notify_postauthor and wp_notify_moderator pluggable functions - * so they point moderation links on emails to Calypso. + if ( ( new Tracking( 'jetpack', $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) { + add_action( 'init', array( new Plugin_Tracking(), 'init' ) ); + } else { + /** + * Initialize tracking right after the user agrees to the terms of service. */ - jetpack_require_lib( 'functions.wp-notify' ); + add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) ); } - } /** @@ -768,10 +945,10 @@ class Jetpack { * @action plugins_loaded */ public function late_initialization() { - add_action( 'plugins_loaded', array( 'Jetpack', 'plugin_textdomain' ), 99 ); add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 ); Partner::init(); + My_Jetpack_Initializer::init(); /** * Fires when Jetpack is fully loaded and ready. This is the point where it's safe @@ -787,48 +964,9 @@ class Jetpack { } /** - * Sets up the XMLRPC request handlers. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::setup_xmlrpc_handlers() - * - * @param Array $request_params Incoming request parameters. - * @param Boolean $is_active Whether the connection is currently active. - * @param Boolean $is_signed Whether the signature check has been successful. - * @param Jetpack_XMLRPC_Server $xmlrpc_server (optional) An instance of the server to use instead of instantiating a new one. - */ - public function setup_xmlrpc_handlers( - $request_params, - $is_active, - $is_signed, - Jetpack_XMLRPC_Server $xmlrpc_server = null - ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::setup_xmlrpc_handlers' ); - return $this->connection_manager->setup_xmlrpc_handlers( - $request_params, - $is_active, - $is_signed, - $xmlrpc_server - ); - } - - /** - * Initialize REST API registration connector. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::initialize_rest_api_registration_connector() - */ - public function initialize_rest_api_registration_connector() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::initialize_rest_api_registration_connector' ); - $this->connection_manager->initialize_rest_api_registration_connector(); - } - - /** * This is ported over from the manage module, which has been deprecated and baked in here. - * - * @param $domains */ - function add_wpcom_to_allowed_redirect_hosts( $domains ) { + public function add_wpcom_to_allowed_redirect_hosts() { add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) ); } @@ -836,7 +974,8 @@ class Jetpack { * Return $domains, with 'wordpress.com' appended. * This is ported over from the manage module, which has been deprecated and baked in here. * - * @param $domains + * @param array $domains Array of domains allowed for redirect. + * * @return array */ function allow_wpcom_domain( $domains ) { @@ -847,6 +986,14 @@ class Jetpack { return array_unique( $domains ); } + /** + * Redirect edit post links to Calypso. + * + * @param string $default_url Post edit URL. + * @param int $post_id Post ID. + * + * @return string + */ function point_edit_post_links_to_calypso( $default_url, $post_id ) { $post = get_post( $post_id ); @@ -857,109 +1004,128 @@ class Jetpack { $post_type = $post->post_type; // Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso. - // https://en.support.wordpress.com/custom-post-types/ + // https://en.support.wordpress.com/custom-post-types/. $allowed_post_types = array( - 'post' => 'post', - 'page' => 'page', - 'jetpack-portfolio' => 'edit/jetpack-portfolio', - 'jetpack-testimonial' => 'edit/jetpack-testimonial', + 'post', + 'page', + 'jetpack-portfolio', + 'jetpack-testimonial', ); - if ( ! in_array( $post_type, array_keys( $allowed_post_types ) ) ) { + if ( ! in_array( $post_type, $allowed_post_types, true ) ) { return $default_url; } - $path_prefix = $allowed_post_types[ $post_type ]; - - $site_slug = self::build_raw_urls( get_home_url() ); - - return esc_url( sprintf( 'https://wordpress.com/%s/%s/%d', $path_prefix, $site_slug, $post_id ) ); + return Redirect::get_url( + 'calypso-edit-' . $post_type, + array( + 'path' => $post_id, + ) + ); } + /** + * Redirect edit comment links to Calypso. + * + * @param string $url Comment edit URL. + * + * @return string + */ function point_edit_comment_links_to_calypso( $url ) { // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID. wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args ); - return esc_url( - sprintf( - 'https://wordpress.com/comment/%s/%d', - self::build_raw_urls( get_home_url() ), - $query_args['amp;c'] + + return Redirect::get_url( + 'calypso-edit-comment', + array( + 'path' => $query_args['amp;c'], ) ); - } - - function jetpack_track_last_sync_callback( $params ) { - /** - * Filter to turn off jitm caching - * - * @since 5.4.0 - * - * @param bool false Whether to cache just in time messages - */ - if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) { - return $params; - } - if ( is_array( $params ) && isset( $params[0] ) ) { - $option = $params[0]; - if ( 'active_plugins' === $option ) { - // use the cache if we can, but not terribly important if it gets evicted - set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS ); - } - } - - return $params; } - function jetpack_connection_banner_callback() { - check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' ); + /** + * Extend Sync callables with Jetpack Plugin functions. + * + * @param array $callables list of callables. + * + * @return array list of callables. + */ + public function filter_sync_callable_whitelist( $callables ) { - if ( isset( $_REQUEST['dismissBanner'] ) ) { - Jetpack_Options::update_option( 'dismissed_connection_banner', 1 ); - wp_send_json_success(); + // Jetpack Functions. + $jetpack_callables = array( + 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ), + 'updates' => array( 'Jetpack', 'get_updates' ), + 'active_modules' => array( 'Jetpack', 'get_active_modules' ), + 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins. + ); + $callables = array_merge( $callables, $jetpack_callables ); + + // Jetpack_SSO_Helpers. + if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) { + $sso_helpers = array( + 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ), + 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ), + 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ), + 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ), + 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ), + ); + $callables = array_merge( $callables, $sso_helpers ); } - wp_die(); + return $callables; } /** - * Removes all XML-RPC methods that are not `jetpack.*`. - * Only used in our alternate XML-RPC endpoint, where we want to - * ensure that Core and other plugins' methods are not exposed. + * Extend Sync multisite callables with Jetpack Plugin functions. * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::remove_non_jetpack_xmlrpc_methods() + * @param array $callables list of callables. * - * @param array $methods A list of registered WordPress XMLRPC methods. - * @return array Filtered $methods + * @return array list of callables. */ - public function remove_non_jetpack_xmlrpc_methods( $methods ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::remove_non_jetpack_xmlrpc_methods' ); - return $this->connection_manager->remove_non_jetpack_xmlrpc_methods( $methods ); + public function filter_sync_multisite_callable_whitelist( $callables ) { + + // Jetpack Funtions. + $jetpack_multisite_callables = array( + 'network_name' => array( 'Jetpack', 'network_name' ), + 'network_allow_new_registrations' => array( 'Jetpack', 'network_allow_new_registrations' ), + 'network_add_new_users' => array( 'Jetpack', 'network_add_new_users' ), + 'network_site_upload_space' => array( 'Jetpack', 'network_site_upload_space' ), + 'network_upload_file_types' => array( 'Jetpack', 'network_upload_file_types' ), + 'network_enable_administration_menus' => array( 'Jetpack', 'network_enable_administration_menus' ), + ); + $callables = array_merge( $callables, $jetpack_multisite_callables ); + + return $callables; } /** - * Since a lot of hosts use a hammer approach to "protecting" WordPress sites, - * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive - * security/firewall policies, we provide our own alternate XML RPC API endpoint - * which is accessible via a different URI. Most of the below is copied directly - * from /xmlrpc.php so that we're replicating it as closely as possible. + * Deprecated + * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead. * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::alternate_xmlrpc() + * @param array $params The action parameters. + * + * @deprecated since 9.8. */ - public function alternate_xmlrpc() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::alternate_xmlrpc' ); - $this->connection_manager->alternate_xmlrpc(); + function jetpack_track_last_sync_callback( $params ) { + _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' ); + return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params ); } - /** - * The callback for the JITM ajax requests. - * - * @deprecated since 7.9.0 - */ - function jetpack_jitm_ajax_callback() { - _deprecated_function( __METHOD__, 'jetpack-7.9' ); + function jetpack_connection_banner_callback() { + check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' ); + + // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set. + if ( + isset( $_REQUEST['dismissBanner'] ) && + ! Jetpack_Connection_Banner::force_display() + ) { + Jetpack_Options::update_option( 'dismissed_connection_banner', 1 ); + wp_send_json_success(); + } + + wp_die(); } /** @@ -971,40 +1137,16 @@ class Jetpack { } } - function jetpack_custom_caps( $caps, $cap, $user_id, $args ) { - $is_development_mode = ( new Status() )->is_development_mode(); + /** + * Sets the Jetpack custom capabilities. + * + * @param string[] $caps Array of the user's capabilities. + * @param string $cap Capability name. + * @param int $user_id The user ID. + * @param array $args Adds the context to the cap. Typically the object ID. + */ + public function jetpack_custom_caps( $caps, $cap, $user_id, $args ) { switch ( $cap ) { - case 'jetpack_connect': - case 'jetpack_reconnect': - if ( $is_development_mode ) { - $caps = array( 'do_not_allow' ); - break; - } - /** - * Pass through. If it's not development mode, these should match disconnect. - * Let users disconnect if it's development mode, just in case things glitch. - */ - case 'jetpack_disconnect': - /** - * In multisite, can individual site admins manage their own connection? - * - * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class. - */ - if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) { - if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) { - /** - * We need to update the option name -- it's terribly unclear which - * direction the override goes. - * - * @todo: Update the option name to `sub-sites-can-manage-own-connections` - */ - $caps = array( 'do_not_allow' ); - break; - } - } - - $caps = array( 'manage_options' ); - break; case 'jetpack_manage_modules': case 'jetpack_activate_modules': case 'jetpack_deactivate_modules': @@ -1027,71 +1169,26 @@ class Jetpack { $caps = array( 'manage_sites' ); break; case 'jetpack_admin_page': - if ( $is_development_mode ) { + $is_offline_mode = ( new Status() )->is_offline_mode(); + if ( $is_offline_mode ) { $caps = array( 'manage_options' ); break; } else { $caps = array( 'read' ); } break; - case 'jetpack_connect_user': - if ( $is_development_mode ) { - $caps = array( 'do_not_allow' ); - break; - } - $caps = array( 'read' ); - break; } return $caps; } /** - * Require a Jetpack authentication. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::require_jetpack_authentication() - */ - public function require_jetpack_authentication() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::require_jetpack_authentication' ); - $this->connection_manager->require_jetpack_authentication(); - } - - /** - * Load language files - * - * @action plugins_loaded - */ - public static function plugin_textdomain() { - // Note to self, the third argument must not be hardcoded, to account for relocated folders. - load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' ); - } - - /** * Register assets for use in various modules and the Jetpack admin page. * * @uses wp_script_is, wp_register_script, plugins_url * @action wp_loaded - * @return null + * @return void */ public function register_assets() { - if ( ! wp_script_is( 'spin', 'registered' ) ) { - wp_register_script( - 'spin', - Assets::get_file_url_for_environment( '_inc/build/spin.min.js', '_inc/spin.js' ), - false, - '1.3' - ); - } - - if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) { - wp_register_script( - 'jquery.spin', - Assets::get_file_url_for_environment( '_inc/build/jquery.spin.min.js', '_inc/jquery.spin.js' ), - array( 'jquery', 'spin' ), - '1.3' - ); - } - if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) { wp_register_script( 'jetpack-gallery-settings', @@ -1115,7 +1212,7 @@ class Jetpack { wp_register_script( 'jetpack-facebook-embed', Assets::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ), - array( 'jquery' ), + array(), null, true ); @@ -1174,10 +1271,10 @@ class Jetpack { } if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { - // WP.com: get_locale() returns 'it' + // WP.com: get_locale() returns 'it'. $locale = GP_Locales::by_slug( $lang ); } else { - // Jetpack: get_locale() returns 'it_IT'; + // Jetpack: get_locale() returns 'it_IT';. $locale = GP_Locales::by_field( 'facebook_locale', $lang ); } @@ -1218,16 +1315,15 @@ class Jetpack { /** * Return the network_site_url so that .com knows what network this site is a part of. * - * @param bool $option * @return string */ - public function jetpack_main_network_site_option( $option ) { + public function jetpack_main_network_site_option() { return network_site_url(); } /** * Network Name. */ - static function network_name( $option = null ) { + public static function network_name() { global $current_site; return $current_site->site_name; } @@ -1236,7 +1332,7 @@ class Jetpack { * * @return string */ - static function network_allow_new_registrations( $option = null ) { + public static function network_allow_new_registrations() { return ( in_array( get_site_option( 'registration' ), array( 'none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration' ) : 'none' ); } /** @@ -1244,7 +1340,7 @@ class Jetpack { * * @return boolian */ - static function network_add_new_users( $option = null ) { + public static function network_add_new_users() { return (bool) get_site_option( 'add_new_users' ); } /** @@ -1253,8 +1349,8 @@ class Jetpack { * * @return number */ - static function network_site_upload_space( $option = null ) { - // value in MB + public static function network_site_upload_space() { + // value in MB. return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() ); } @@ -1263,7 +1359,7 @@ class Jetpack { * * @return string */ - static function network_upload_file_types( $option = null ) { + public static function network_upload_file_types() { return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ); } @@ -1272,8 +1368,8 @@ class Jetpack { * * @return number */ - static function network_max_upload_file_size( $option = null ) { - // value in KB + public static function network_max_upload_file_size() { + // value in KB. return get_site_option( 'fileupload_maxk', 300 ); } @@ -1282,7 +1378,7 @@ class Jetpack { * * @return array */ - static function network_enable_administration_menus( $option = null ) { + public static function network_enable_administration_menus() { return get_site_option( 'menu_items' ); } @@ -1322,7 +1418,7 @@ class Jetpack { if ( count( $admins ) > 1 ) { $available = array(); foreach ( $admins as $admin ) { - if ( self::is_user_connected( $admin->ID ) ) { + if ( self::connection()->is_user_connected( $admin->ID ) ) { $available[] = $admin->ID; } } @@ -1350,22 +1446,19 @@ class Jetpack { * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the * database which could be set to anything as opposed to what this function returns. * - * @param bool $option - * * @return boolean */ - public function is_main_network_option( $option ) { - // return '1' or '' + public function is_main_network_option() { + // returns either an '1' or an empty string. return (string) (bool) self::is_multi_network(); } /** * Return true if we are with multi-site or multi-network false if we are dealing with single site. * - * @param string $option - * @return boolean + * @return string */ - public function is_multisite( $option ) { + public function is_multisite() { return (string) (bool) is_multisite(); } @@ -1379,7 +1472,7 @@ class Jetpack { public static function is_multi_network() { global $wpdb; - // if we don't have a multi site setup no need to do any more + // if we don't have a multi site setup no need to do any more. if ( ! is_multisite() ) { return false; } @@ -1393,22 +1486,6 @@ class Jetpack { } /** - * Trigger an update to the main_network_site when we update the siteurl of a site. - * - * @return null - */ - function update_jetpack_main_network_site_option() { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - } - /** - * Triggered after a user updates the network settings via Network Settings Admin Page - */ - function update_jetpack_network_settings() { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - // Only sync this info for the main network site. - } - - /** * Get back if the current site is single user site. * * @return bool @@ -1449,56 +1526,16 @@ class Jetpack { return 0; } +// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled /** - * Finds out if a site is using a version control system. - * - * @return string ( '1' | '0' ) - **/ - public static function is_version_controlled() { - _deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' ); - return (string) (int) Functions::is_version_controlled(); - } - - /** - * Determines whether the current theme supports featured images or not. - * - * @return string ( '1' | '0' ) - */ - public static function featured_images_enabled() { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - return current_theme_supports( 'post-thumbnails' ) ? '1' : '0'; - } - - /** - * Wrapper for core's get_avatar_url(). This one is deprecated. + * Gets updates and stores in jetpack_updates. * - * @deprecated 4.7 use get_avatar_url instead. - * @param int|string|object $id_or_email A user ID, email address, or comment object - * @param int $size Size of the avatar image - * @param string $default URL to a default image to use if no avatar is available - * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled - * - * @return array - */ - public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) { - _deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' ); - return get_avatar_url( - $id_or_email, - array( - 'size' => $size, - 'default' => $default, - 'force_default' => $force_display, - ) - ); - } - - /** - * jetpack_updates is saved in the following schema: + * The jetpack_updates option is saved in the following schema: * * array ( * 'plugins' => (int) Number of plugin updates available. * 'themes' => (int) Number of theme updates available. - * 'wordpress' => (int) Number of WordPress core updates available. // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled + * 'wordpress' => (int) Number of WordPress core updates available. * 'translations' => (int) Number of translation updates available. * 'total' => (int) Total of all available updates. * 'wp_update_version' => (string) The latest available version of WordPress, only present if a WordPress update is needed. @@ -1523,7 +1560,13 @@ class Jetpack { } return isset( $updates ) ? $updates : array(); } + // phpcs:enable + /** + * Get update details for core, plugins, and themes. + * + * @return array + */ public static function get_update_details() { $update_details = array( 'update_core' => get_site_transient( 'update_core' ), @@ -1533,54 +1576,55 @@ class Jetpack { return $update_details; } - public static function refresh_update_data() { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - - } - - public static function refresh_theme_data() { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - } - /** * Is Jetpack active? + * The method only checks if there's an existing token for the master user. It doesn't validate the token. + * + * This method is deprecated since 9.6.0. Please use one of the methods provided by the Manager class in the Connection package, + * or Jetpack::is_connection_ready if you want to know when the Jetpack plugin starts considering the connection ready to be used. + * + * Since this method has a wide spread use, we decided not to throw any deprecation warnings for now. + * + * @deprecated 9.6.0 + * + * @return bool */ public static function is_active() { - return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER ); + return self::connection()->is_active(); } /** - * Make an API call to WordPress.com for plan status + * Returns true if the current site is connected to WordPress.com and has the minimum requirements to enable Jetpack UI * - * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom. + * This method was introduced just before the release of the possibility to use Jetpack without a user connection, while + * it was available only when no_user_testing_mode was enabled. In the near future, this will return is_connected for all + * users and this option will be available by default for everybody. * - * @return bool True if plan is updated, false if no update - */ - public static function refresh_active_plan_from_wpcom() { - _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' ); - return Jetpack_Plan::refresh_from_wpcom(); - } - - /** - * Get the plan that this Jetpack site is currently using + * @since 9.6.0 + * @since 9.7.0 returns is_connected in all cases and adds filter to the returned value * - * @deprecated 7.2.0 Use Jetpack_Plan::get. - * @return array Active Jetpack plan details. + * @return bool is the site connection ready to be used? */ - public static function get_active_plan() { - _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' ); - return Jetpack_Plan::get(); - } + public static function is_connection_ready() { + $is_connected = false; - /** - * Determine whether the active plan supports a particular feature - * - * @deprecated 7.2.0 Use Jetpack_Plan::supports. - * @return bool True if plan supports feature, false if not. - */ - public static function active_plan_supports( $feature ) { - _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' ); - return Jetpack_Plan::supports( $feature ); + if ( method_exists( self::connection(), 'is_connected' ) ) { + $is_connected = self::connection()->is_connected(); + } elseif ( method_exists( self::connection(), 'is_registered' ) ) { + $is_connected = self::connection()->is_registered(); + } + + /** + * Allows filtering whether the connection is ready to be used. If true, this will enable the Jetpack UI and modules + * + * Modules will be enabled depending on the connection status and if the module requires a connection or user connection. + * + * @since 9.7.0 + * + * @param bool $is_connection_ready Is the connection ready? + * @param Automattic\Jetpack\Connection\Manager $connection_manager Instance of the Manager class, can be used to check the connection status. + */ + return apply_filters( 'jetpack_is_connection_ready', $is_connected, self::connection() ); } /** @@ -1589,12 +1633,13 @@ class Jetpack { * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins * and themes still use it, and we do not want to flood them with notices. * - * Please use Automattic\Jetpack\Status()->is_development_mode() instead. + * Please use Automattic\Jetpack\Status()->is_offline_mode() instead. * * @deprecated since 8.0. */ public static function is_development_mode() { - return ( new Status() )->is_development_mode(); + _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' ); + return ( new Status() )->is_offline_mode(); } /** @@ -1613,35 +1658,42 @@ class Jetpack { } /** - * Determines reason for Jetpack development mode. + * Determines reason for Jetpack offline mode. */ public static function development_mode_trigger_text() { - if ( ! ( new Status() )->is_development_mode() ) { - return __( 'Jetpack is not in Development Mode.', 'jetpack' ); + $status = new Status(); + + if ( ! $status->is_offline_mode() ) { + return __( 'Jetpack is not in Offline Mode.', 'jetpack' ); } if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) { $notice = __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' ); - } elseif ( site_url() && false === strpos( site_url(), '.' ) ) { - $notice = __( 'The site URL lacking a dot (e.g. http://localhost).', 'jetpack' ); - } else { + } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) { + $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' ); + } elseif ( $status->is_local_site() ) { + $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' ); + /** This filter is documented in packages/status/src/class-status.php */ + } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name. $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' ); + } else { + $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' ); } return $notice; } /** - * Get Jetpack development mode notice text and notice class. + * Get Jetpack offline mode notice text and notice class. * - * Mirrors the checks made in Automattic\Jetpack\Status->is_development_mode + * Mirrors the checks made in Automattic\Jetpack\Status->is_offline_mode */ public static function show_development_mode_notice() { - if ( ( new Status() )->is_development_mode() ) { + if ( ( new Status() )->is_offline_mode() ) { $notice = sprintf( /* translators: %s is a URL */ - __( 'In <a href="%s" target="_blank">Development Mode</a>:', 'jetpack' ), - 'https://jetpack.com/support/development-mode/' + __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ), + Redirect::get_url( 'jetpack-support-development-mode' ) ); $notice .= ' ' . self::development_mode_trigger_text(); @@ -1652,14 +1704,14 @@ class Jetpack { // Throw up a notice if using a development version and as for feedback. if ( self::is_development_version() ) { /* translators: %s is a URL */ - $notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), 'https://jetpack.com/contact-support/beta-group/' ); + $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' ) ); echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; } - // Throw up a notice if using staging mode + // Throw up a notice if using staging mode. if ( ( new Status() )->is_staging_site() ) { /* translators: %s is a URL */ - $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.com/support/staging-sites/' ); + $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) ); echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>'; } @@ -1686,43 +1738,28 @@ class Jetpack { /** * Is a given user (or the current user if none is specified) linked to a WordPress.com user? + * + * @param int $user_id User ID or will use get_current_user_id if false/not provided. */ public static function is_user_connected( $user_id = false ) { + _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' ); return self::connection()->is_user_connected( $user_id ); } /** * Get the wpcom user data of the current|specified connected user. + * + * @param null|int $user_id User ID or will use get_current_user_id if null. */ public static function get_connected_user_data( $user_id = null ) { - // TODO: remove in favor of Connection_Manager->get_connected_user_data - if ( ! $user_id ) { - $user_id = get_current_user_id(); - } - - $transient_key = "jetpack_connected_user_data_$user_id"; - - if ( $cached_user_data = get_transient( $transient_key ) ) { - return $cached_user_data; - } - - $xml = new Jetpack_IXR_Client( - array( - 'user_id' => $user_id, - ) - ); - $xml->query( 'wpcom.getUser' ); - if ( ! $xml->isError() ) { - $user_data = $xml->getResponse(); - set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS ); - return $user_data; - } - - return false; + _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' ); + return self::connection()->get_connected_user_data( $user_id ); } /** * Get the wpcom email of the current|specified connected user. + * + * @param null|int $user_id User ID or will use get_current_user_id if null. */ public static function get_connected_user_email( $user_id = null ) { if ( ! $user_id ) { @@ -1753,18 +1790,6 @@ class Jetpack { } /** - * Whether the current user is the connection owner. - * - * @deprecated since 7.7 - * - * @return bool Whether the current user is the connection owner. - */ - public function current_user_is_connection_owner() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::is_connection_owner' ); - return self::connection()->is_connection_owner(); - } - - /** * Gets current user IP address. * * @param bool $check_all_headers Check all headers? Default is `false`. @@ -1793,35 +1818,13 @@ class Jetpack { } /** - * Add any extra oEmbed providers that we know about and use on wpcom for feature parity. - */ - function extra_oembed_providers() { - // Cloudup: https://dev.cloudup.com/#oembed - wp_oembed_add_provider( 'https://cloudup.com/*', 'https://cloudup.com/oembed' ); - wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' ); - wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true ); - wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true ); - wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true ); - wp_oembed_add_provider( '#https?://(www\.)?icloud\.com/keynote/.*#i', 'https://iwmb.icloud.com/iwmb/oembed', true ); - wp_oembed_add_provider( 'https://song.link/*', 'https://song.link/oembed', false ); - } - - /** - * Synchronize connected user role changes - */ - function user_role_change( $user_id ) { - _deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' ); - Users::user_role_change( $user_id ); - } - - /** * Loads the currently active modules. */ public static function load_modules() { - $is_development_mode = ( new Status() )->is_development_mode(); + $is_offline_mode = ( new Status() )->is_offline_mode(); if ( - ! self::is_active() - && ! $is_development_mode + ! self::is_connection_ready() + && ! $is_offline_mode && ! self::is_onboarding() && ( ! is_multisite() @@ -1863,15 +1866,25 @@ class Jetpack { $modules = array_diff( $modules, $updated_modules ); } + $is_site_connection = false; + + if ( method_exists( self::connection(), 'is_site_connection' ) ) { + $is_site_connection = self::connection()->is_site_connection(); + } + foreach ( $modules as $index => $module ) { - // If we're in dev mode, disable modules requiring a connection - if ( $is_development_mode ) { - // Prime the pump if we need to + // If we're in offline/site-connection mode, disable modules requiring a connection/user connection. + if ( $is_offline_mode || $is_site_connection ) { + // Prime the pump if we need to. if ( empty( $modules_data[ $module ] ) ) { $modules_data[ $module ] = self::get_module( $module ); } // If the module requires a connection, but we're in local mode, don't include it. - if ( $modules_data[ $module ]['requires_connection'] ) { + if ( $is_offline_mode && $modules_data[ $module ]['requires_connection'] ) { + continue; + } + + if ( $is_site_connection && $modules_data[ $module ]['requires_user_connection'] ) { continue; } } @@ -1910,7 +1923,7 @@ class Jetpack { * Check if Jetpack's REST API compat file should be included * * @action plugins_loaded - * @return null + * @return void */ public function check_rest_api_compat() { /** @@ -1922,10 +1935,6 @@ class Jetpack { */ $_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() ); - if ( function_exists( 'bbpress' ) ) { - $_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php'; - } - foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include ) { require_once $_jetpack_rest_api_compat_include; } @@ -2018,6 +2027,8 @@ class Jetpack { * * We don't want to store these in a static variable, in case * there are switch_to_blog() calls involved. + * + * @param string $plugin Plugin to check in 'folder/file.php` format. */ public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) { return in_array( $plugin, self::get_active_plugins() ); @@ -2029,7 +2040,7 @@ class Jetpack { * * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters * @action plugins_loaded - * @return null + * @return void */ public function check_open_graph() { if ( in_array( 'publicize', self::get_active_modules() ) || in_array( 'sharedaddy', self::get_active_modules() ) ) { @@ -2065,7 +2076,7 @@ class Jetpack { * * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters * @action plugins_loaded - * @return null + * @return void */ public function check_twitter_tags() { @@ -2092,81 +2103,29 @@ class Jetpack { } } - /** - * Allows plugins to submit security reports. - * - * @param string $type Report type (login_form, backup, file_scanning, spam) - * @param string $plugin_file Plugin __FILE__, so that we can pull plugin data - * @param array $args See definitions above - */ - public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) { - _deprecated_function( __FUNCTION__, 'jetpack-4.2', null ); - } - /* Jetpack Options API */ - public static function get_option_names( $type = 'compact' ) { - return Jetpack_Options::get_option_names( $type ); - } - /** - * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate. + * Gets the option names from Jetpack_Options. * - * @param string $name Option name - * @param mixed $default (optional) - */ - public static function get_option( $name, $default = false ) { - return Jetpack_Options::get_option( $name, $default ); - } - - /** - * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate. - * - * @deprecated 3.4 use Jetpack_Options::update_option() instead. - * @param string $name Option name - * @param mixed $value Option value - */ - public static function update_option( $name, $value ) { - _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' ); - return Jetpack_Options::update_option( $name, $value ); - } - - /** - * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate. - * - * @deprecated 3.4 use Jetpack_Options::update_options() instead. - * @param array $array array( option name => option value, ... ) - */ - public static function update_options( $array ) { - _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' ); - return Jetpack_Options::update_options( $array ); - } - - /** - * Deletes the given option. May be passed multiple option names as an array. - * Updates jetpack_options and/or deletes jetpack_$name as appropriate. + * @param string $type Jetpack option type. * - * @deprecated 3.4 use Jetpack_Options::delete_option() instead. - * @param string|array $names + * @return array */ - public static function delete_option( $names ) { - _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' ); - return Jetpack_Options::delete_option( $names ); + public static function get_option_names( $type = 'compact' ) { + return Jetpack_Options::get_option_names( $type ); } /** - * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::update_user_token() instead. + * Returns the requested option. * - * Enters a user token into the user_tokens option + * Looks in jetpack_options or jetpack_$name as appropriate. * - * @param int $user_id The user id. - * @param string $token The user token. - * @param bool $is_master_user Whether the user is the master user. - * @return bool + * @param string $name Option name. + * @param mixed $default Default value. */ - public static function update_user_token( $user_id, $token, $is_master_user ) { - _deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::update_user_token' ); - return Connection_Utils::update_user_token( $user_id, $token, $is_master_user ); + public static function get_option( $name, $default = false ) { + return Jetpack_Options::get_option( $name, $default ); } /** @@ -2206,12 +2165,19 @@ class Jetpack { return $files; } + /** + * Activate new modules. + * + * @param bool $redirect Should this function redirect after activation. + * + * @return void + */ public static function activate_new_modules( $redirect = false ) { - if ( ! self::is_active() && ! ( new Status() )->is_development_mode() ) { + if ( ! self::is_connection_ready() && ! ( new Status() )->is_offline_mode() ) { return; } - $jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic] + $jetpack_old_version = Jetpack_Options::get_option( 'version' ); if ( ! $jetpack_old_version ) { $jetpack_old_version = $version = $old_version = '1.1:' . time(); /** This action is documented in class.jetpack.php */ @@ -2219,7 +2185,7 @@ class Jetpack { Jetpack_Options::update_options( compact( 'version', 'old_version' ) ); } - list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic] + list( $jetpack_version ) = explode( ':', $jetpack_old_version ); if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) { return; @@ -2252,10 +2218,11 @@ class Jetpack { ); self::state( 'message', 'modules_activated' ); + self::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules, $redirect ); if ( $redirect ) { - $page = 'jetpack'; // make sure we redirect to either settings or the jetpack page + $page = 'jetpack'; // make sure we redirect to either settings or the jetpack page. if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) { $page = $_GET['page']; } @@ -2268,8 +2235,15 @@ class Jetpack { /** * List available Jetpack modules. Simply lists .php files in /modules/. * Make sure to tuck away module "library" files in a sub-directory. + * + * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter. + * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter. + * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection. + * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection. + * + * @return array $modules Array of module slugs */ - public static function get_available_modules( $min_version = false, $max_version = false ) { + public static function get_available_modules( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) { static $modules = null; if ( ! isset( $modules ) ) { @@ -2283,11 +2257,14 @@ class Jetpack { $modules = array(); foreach ( $files as $file ) { - if ( ! $headers = self::get_module( $file ) ) { + $slug = self::get_module_slug( $file ); + $headers = self::get_module( $slug ); + + if ( ! $headers ) { continue; } - $modules[ self::get_module_slug( $file ) ] = $headers['introduced']; + $modules[ $slug ] = $headers['introduced']; } Jetpack_Options::update_option( @@ -2307,10 +2284,12 @@ class Jetpack { * @param array $modules Array of available modules. * @param string $min_version Minimum version number required to use modules. * @param string $max_version Maximum version number required to use modules. + * @param bool|null $requires_connection Value of the Requires Connection filter. + * @param bool|null $requires_user_connection Value of the Requires User Connection filter. */ - $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version ); + $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version, $requires_connection, $requires_user_connection ); - if ( ! $min_version && ! $max_version ) { + if ( ! $min_version && ! $max_version && is_null( $requires_connection ) && is_null( $requires_user_connection ) ) { return array_keys( $mods ); } @@ -2324,6 +2303,16 @@ class Jetpack { continue; } + $mod_details = self::get_module( $slug ); + + if ( null !== $requires_connection && (bool) $requires_connection !== $mod_details['requires_connection'] ) { + continue; + } + + if ( null !== $requires_user_connection && (bool) $requires_user_connection !== $mod_details['requires_user_connection'] ) { + continue; + } + $r[] = $slug; } @@ -2331,12 +2320,19 @@ class Jetpack { } /** - * Default modules loaded on activation. + * Get default modules loaded on activation. + * + * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter. + * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter. + * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection. + * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection. + * + * @return array $modules Array of module slugs */ - public static function get_default_modules( $min_version = false, $max_version = false ) { + public static function get_default_modules( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) { $return = array(); - foreach ( self::get_available_modules( $min_version, $max_version ) as $module ) { + foreach ( self::get_available_modules( $min_version, $max_version, $requires_connection, $requires_user_connection ) as $module ) { $module_data = self::get_module( $module ); switch ( strtolower( $module_data['auto_activate'] ) ) { @@ -2361,8 +2357,10 @@ class Jetpack { * @param array $return Array of default modules. * @param string $min_version Minimum version number required to use modules. * @param string $max_version Maximum version number required to use modules. + * @param bool|null $requires_connection Value of the Requires Connection filter. + * @param bool|null $requires_user_connection Value of the Requires User Connection filter. */ - return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version ); + return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version, $requires_connection, $requires_user_connection ); } /** @@ -2379,7 +2377,7 @@ class Jetpack { * * @since 2.6 * @uses jetpack_get_default_modules filter - * @param array $modules + * @param array $modules Array of Jetpack modules. * @return array */ function handle_deprecated_modules( $modules ) { @@ -2387,6 +2385,7 @@ class Jetpack { 'debug' => null, // Closed out and moved to the debugger library. 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality. 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support. + 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP. ); // Don't activate SSO if they never completed activating WPCC. @@ -2417,7 +2416,7 @@ class Jetpack { * * @since 2.6 * @uses jetpack_get_default_modules filter - * @param array $modules + * @param array $modules Array of Jetpack modules. * @return array */ function filter_default_modules( $modules ) { @@ -2447,6 +2446,10 @@ class Jetpack { /** * Extract a module's slug from its full path. + * + * @param string $file Full path to a file. + * + * @return string Module slug. */ public static function get_module_slug( $file ) { return str_replace( '.php', '', basename( $file ) ); @@ -2454,6 +2457,8 @@ class Jetpack { /** * Generate a module's path from its slug. + * + * @param string $slug Module slug. */ public static function get_module_path( $slug ) { /** @@ -2471,64 +2476,70 @@ class Jetpack { * Load module data from module file. Headers differ from WordPress * plugin headers to avoid them being identified as standalone * plugins on the WordPress plugins page. + * + * @param string $module The module slug. */ public static function get_module( $module ) { - $headers = array( - 'name' => 'Module Name', - 'description' => 'Module Description', - 'sort' => 'Sort Order', - 'recommendation_order' => 'Recommendation Order', - 'introduced' => 'First Introduced', - 'changed' => 'Major Changes In', - 'deactivate' => 'Deactivate', - 'free' => 'Free', - 'requires_connection' => 'Requires Connection', - 'auto_activate' => 'Auto Activate', - 'module_tags' => 'Module Tags', - 'feature' => 'Feature', - 'additional_search_queries' => 'Additional Search Queries', - 'plan_classes' => 'Plans', - ); - - $file = self::get_module_path( self::get_module_slug( $module ) ); + static $modules_details; - $mod = self::get_file_data( $file, $headers ); - if ( empty( $mod['name'] ) ) { + if ( jetpack_has_no_module_info( $module ) ) { return false; } - $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort']; - $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order']; - $mod['deactivate'] = empty( $mod['deactivate'] ); - $mod['free'] = empty( $mod['free'] ); - $mod['requires_connection'] = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true; + $file = self::get_module_path( self::get_module_slug( $module ) ); - if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) { - $mod['auto_activate'] = 'No'; + if ( isset( $modules_details[ $module ] ) ) { + $mod = $modules_details[ $module ]; } else { - $mod['auto_activate'] = (string) $mod['auto_activate']; - } + $mod = jetpack_get_module_info( $module ); - if ( $mod['module_tags'] ) { - $mod['module_tags'] = explode( ',', $mod['module_tags'] ); - $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] ); - $mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] ); - } else { - $mod['module_tags'] = array( self::translate_module_tag( 'Other' ) ); - } + if ( null === $mod ) { + // Try to get the module info from the file as a fallback. + $mod = self::get_file_data( $file, jetpack_get_all_module_header_names() ); - if ( $mod['plan_classes'] ) { - $mod['plan_classes'] = explode( ',', $mod['plan_classes'] ); - $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) ); - } else { - $mod['plan_classes'] = array( 'free' ); - } + if ( empty( $mod['name'] ) ) { + // No info for this module. + return false; + } + } + + $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort']; + $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order']; + $mod['deactivate'] = empty( $mod['deactivate'] ); + $mod['free'] = empty( $mod['free'] ); + $mod['requires_connection'] = ( ! empty( $mod['requires_connection'] ) && 'No' === $mod['requires_connection'] ) ? false : true; + $mod['requires_user_connection'] = ( empty( $mod['requires_user_connection'] ) || 'No' === $mod['requires_user_connection'] ) ? false : true; + + if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ), true ) ) { + $mod['auto_activate'] = 'No'; + } else { + $mod['auto_activate'] = (string) $mod['auto_activate']; + } + + if ( $mod['module_tags'] ) { + $mod['module_tags'] = explode( ',', $mod['module_tags'] ); + $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] ); + $mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] ); + } else { + $mod['module_tags'] = array( self::translate_module_tag( 'Other' ) ); + } + + if ( $mod['plan_classes'] ) { + $mod['plan_classes'] = explode( ',', $mod['plan_classes'] ); + $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) ); + } else { + $mod['plan_classes'] = array( 'free' ); + } + + if ( $mod['feature'] ) { + $mod['feature'] = explode( ',', $mod['feature'] ); + $mod['feature'] = array_map( 'trim', $mod['feature'] ); + } else { + $mod['feature'] = array( self::translate_module_tag( 'Other' ) ); + } + + $modules_details[ $module ] = $mod; - if ( $mod['feature'] ) { - $mod['feature'] = explode( ',', $mod['feature'] ); - $mod['feature'] = array_map( 'trim', $mod['feature'] ); - } else { - $mod['feature'] = array( self::translate_module_tag( 'Other' ) ); } /** @@ -2564,9 +2575,12 @@ class Jetpack { /** * Like core's get_file_data implementation, but caches the result. + * + * @param string $file Absolute path to the file. + * @param array $headers List of headers, in the format array( 'HeaderKey' => 'Header Name' ). */ public static function get_file_data( $file, $headers ) { - // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated + // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated. $file_name = basename( $file ); $cache_key = 'jetpack_file_data_' . JETPACK__VERSION; @@ -2599,7 +2613,6 @@ class Jetpack { return $data; } - /** * Return translated module tag. * @@ -2612,68 +2625,11 @@ class Jetpack { } /** - * Get i18n strings as a JSON-encoded string - * - * @return string The locale as JSON - */ - public static function get_i18n_data_json() { - - // WordPress 5.0 uses md5 hashes of file paths to associate translation - // JSON files with the file they should be included for. This is an md5 - // of '_inc/build/admin.js'. - $path_md5 = '1bac79e646a8bf4081a5011ab72d5807'; - - $i18n_json = - JETPACK__PLUGIN_DIR - . 'languages/json/jetpack-' - . get_user_locale() - . '-' - . $path_md5 - . '.json'; - - if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) { - $locale_data = @file_get_contents( $i18n_json ); - if ( $locale_data ) { - return $locale_data; - } - } - - // Return valid empty Jed locale - return '{ "locale_data": { "messages": { "": {} } } }'; - } - - /** - * Add locale data setup to wp-i18n - * - * Any Jetpack script that depends on wp-i18n should use this method to set up the locale. - * - * The locale setup depends on an adding inline script. This is error-prone and could easily - * result in multiple additions of the same script when exactly 0 or 1 is desireable. - * - * This method provides a safe way to request the setup multiple times but add the script at - * most once. - * - * @since 6.7.0 - * - * @return void - */ - public static function setup_wp_i18n_locale_data() { - static $script_added = false; - if ( ! $script_added ) { - $script_added = true; - wp_add_inline_script( - 'wp-i18n', - 'wp.i18n.setLocaleData( ' . self::get_i18n_data_json() . ', \'jetpack\' );' - ); - } - } - - /** * Return module name translation. Uses matching string created in modules/module-headings.php. * * @since 3.9.2 * - * @param array $modules + * @param array $modules Array of Jetpack modules. * * @return string|void */ @@ -2739,6 +2695,13 @@ class Jetpack { return in_array( $module, self::get_active_modules() ); } + /** + * Is slug a valid module. + * + * @param string $module Module slug. + * + * @return bool + */ public static function is_module( $module ) { return ! empty( $module ) && ! validate_file( $module, self::get_available_modules() ); } @@ -2777,7 +2740,7 @@ class Jetpack { * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR. * - * @param $string + * @param string $string String to attempt rewrite. * @return mixed */ public static function alias_directories( $string ) { @@ -2789,12 +2752,27 @@ class Jetpack { return $string; } + /** + * Activates default Jetpack modules. + * + * @param null|string $min_version Only return modules introduced in this version or later. Default is false, do not filter. + * @param null|string $max_version Only return modules introduced before this version. Default is false, do not filter. + * @param array $other_modules Other modules to activate. + * @param null|bool $redirect Should there be a redirection after activation. + * @param bool $send_state_messages If a state message should be sent. + * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection. + * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection. + * + * @return void + */ public static function activate_default_modules( $min_version = false, $max_version = false, $other_modules = array(), $redirect = null, - $send_state_messages = null + $send_state_messages = null, + $requires_connection = null, + $requires_user_connection = null ) { $jetpack = self::init(); @@ -2822,7 +2800,7 @@ class Jetpack { $send_state_messages = current_user_can( 'jetpack_activate_modules' ); } - $modules = self::get_default_modules( $min_version, $max_version ); + $modules = self::get_default_modules( $min_version, $max_version, $requires_connection, $requires_user_connection ); $modules = array_merge( $other_modules, $modules ); // Look for standalone plugins and disable if active. @@ -2865,13 +2843,15 @@ class Jetpack { * * @since 1.9.0 * - * @param string $min_version Minimum version number required to use modules. - * @param string $max_version Maximum version number required to use modules. - * @param array $other_modules Array of other modules to activate alongside the default modules. + * @param string $min_version Minimum version number required to use modules. + * @param string $max_version Maximum version number required to use modules. + * @param array $other_modules Array of other modules to activate alongside the default modules. + * @param bool|null $requires_connection Value of the Requires Connection filter. + * @param bool|null $requires_user_connection Value of the Requires User Connection filter. */ - do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules ); + do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules, $requires_connection, $requires_user_connection ); - // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating + // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating. if ( $send_state_messages ) { self::restate(); self::catch_errors( true ); @@ -2906,7 +2886,7 @@ class Jetpack { continue; } - // we'll override this later if the plugin can be included without fatal error + // we'll override this later if the plugin can be included without fatal error. if ( $redirect ) { wp_safe_redirect( self::admin_url( 'page=jetpack' ) ); } @@ -2949,13 +2929,24 @@ class Jetpack { * * @since 1.9.0 * - * @param string $min_version Minimum version number required to use modules. - * @param string $max_version Maximum version number required to use modules. - * @param array $other_modules Array of other modules to activate alongside the default modules. + * @param string $min_version Minimum version number required to use modules. + * @param string $max_version Maximum version number required to use modules. + * @param array $other_modules Array of other modules to activate alongside the default modules. + * @param bool|null $requires_connection Value of the Requires Connection filter. + * @param bool|null $requires_user_connection Value of the Requires User Connection filter. */ - do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules ); + do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules, $requires_connection, $requires_user_connection ); } + /** + * Activate a module. + * + * @param string $module Module slug. + * @param bool $exit Should exit be called after deactivation. + * @param bool $redirect Should there be a redirection after activation. + * + * @return bool|void + */ public static function activate_module( $module, $exit = true, $redirect = true ) { /** * Fires before a module is activated. @@ -2978,7 +2969,7 @@ class Jetpack { return false; } - // If it's already active, then don't do it again + // If it's already active, then don't do it again. $active = self::get_active_modules(); foreach ( $active as $act ) { if ( $act == $module ) { @@ -2988,21 +2979,21 @@ class Jetpack { $module_data = self::get_module( $module ); - $is_development_mode = ( new Status() )->is_development_mode(); - if ( ! self::is_active() ) { - if ( ! $is_development_mode && ! self::is_onboarding() ) { + $is_offline_mode = ( new Status() )->is_offline_mode(); + if ( ! self::is_connection_ready() ) { + if ( ! $is_offline_mode && ! self::is_onboarding() ) { return false; } - // If we're not connected but in development mode, make sure the module doesn't require a connection - if ( $is_development_mode && $module_data['requires_connection'] ) { + // If we're not connected but in offline mode, make sure the module doesn't require a connection. + if ( $is_offline_mode && $module_data['requires_connection'] ) { return false; } } - // Check and see if the old plugin is active + // Check and see if the old plugin is active. if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) { - // Deactivate the old plugin + // Deactivate the old plugin. if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) { // If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module // We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load. @@ -3012,7 +3003,7 @@ class Jetpack { } } - // Protect won't work with mis-configured IPs + // Protect won't work with mis-configured IPs. if ( 'protect' === $module ) { include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php'; if ( ! jetpack_protect_get_ip() ) { @@ -3025,9 +3016,9 @@ class Jetpack { return false; } - // Check the file for fatal errors, a la wp-admin/plugins.php::activate + // Check the file for fatal errors, a la wp-admin/plugins.php::activate. self::state( 'module', $module ); - self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error + self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error. self::catch_errors( true ); ob_start(); @@ -3037,7 +3028,7 @@ class Jetpack { $active[] = $module; self::update_active_modules( $active ); - self::state( 'error', false ); // the override + self::state( 'error', false ); // the override. ob_end_clean(); self::catch_errors( false ); @@ -3050,10 +3041,13 @@ class Jetpack { return true; } - function activate_module_actions( $module ) { - _deprecated_function( __METHOD__, 'jetpack-4.2' ); - } - + /** + * Deactivate module. + * + * @param string $module Module slug. + * + * @return bool + */ public static function deactivate_module( $module ) { /** * Fires when a module is deactivated. @@ -3072,6 +3066,13 @@ class Jetpack { return self::update_active_modules( $new ); } + /** + * Enable a configuable module. + * + * @param string $module Module slug. + * + * @return void + */ public static function enable_module_configurable( $module ) { $module = self::get_module_slug( $module ); add_filter( 'jetpack_module_configurable_' . $module, '__return_true' ); @@ -3081,8 +3082,8 @@ class Jetpack { * Composes a module configure URL. It uses Jetpack settings search as default value * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter * - * @param string $module Module slug - * @return string $url module configuration URL + * @param string $module Module slug. + * @return string $url module configuration URL. */ public static function module_configuration_url( $module ) { $module = self::get_module_slug( $module ); @@ -3100,6 +3101,14 @@ class Jetpack { } /* Installation */ + /** + * Bail on activation if there is an issue. + * + * @param string $message Error message. + * @param bool $deactivate Deactivate Jetpack or not. + * + * @return void + */ public static function bail_on_activation( $message, $deactivate = true ) { ?> <!doctype html> @@ -3144,12 +3153,13 @@ p { /** * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook() * - * @static + * @param bool $network_wide Network-wide activation. */ public static function plugin_activation( $network_wide ) { Jetpack_Options::update_option( 'activated', 1 ); if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) { + /* translator: Jetpack version number. */ self::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) ); } @@ -3157,16 +3167,28 @@ p { self::state( 'network_nag', true ); } - // For firing one-off events (notices) immediately after activation - set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS ); + // For firing one-off events (notices) immediately after activation. + set_transient( 'activated_jetpack', true, 0.1 * MINUTE_IN_SECONDS ); update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) ); + Health::on_jetpack_activated(); + + if ( self::is_connection_ready() && method_exists( 'Automattic\Jetpack\Sync\Actions', 'do_only_first_initial_sync' ) ) { + Sync_Actions::do_only_first_initial_sync(); + } + self::plugin_initialize(); } + /** + * Returns the activation source. + * + * @param string $referer_url URL. + * + * @return array source_type, source_query. + */ public static function get_activation_source( $referer_url ) { - if ( defined( 'WP_CLI' ) && WP_CLI ) { return array( 'wp-cli', null ); } @@ -3176,7 +3198,7 @@ p { $source_type = 'unknown'; $source_query = null; - if ( ! is_array( $referer ) ) { + if ( ! is_array( $referer ) || ! isset( $referer['path'] ) ) { return array( $source_type, $source_query ); } @@ -3218,13 +3240,37 @@ p { /** * Runs before bumping version numbers up to a new version * - * @param string $version Version:timestamp - * @param string $old_version Old Version:timestamp or false if not set yet. - * @return null [description] + * @param string $version Version:timestamp. + * @param string $old_version Old Version:timestamp or false if not set yet. */ public static function do_version_bump( $version, $old_version ) { - if ( ! $old_version ) { // For new sites - // There used to be stuff here, but this seems like it might be useful to someone in the future... + if ( $old_version ) { // For existing Jetpack installations. + add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' ); + + // If a front end page is visited after the update, the 'wp' action will fire. + add_action( 'wp', 'Jetpack::set_update_modal_display' ); + + // If an admin page is visited after the update, the 'current_screen' action will fire. + add_action( 'current_screen', 'Jetpack::set_update_modal_display' ); + } + } + + /** + * Sets the display_update_modal state. + */ + public static function set_update_modal_display() { + self::state( 'display_update_modal', true ); + + } + + /** + * Enqueues the block library styles. + * + * @param string $hook The current admin page. + */ + public static function enqueue_block_style( $hook ) { + if ( 'toplevel_page_jetpack' === $hook ) { + wp_enqueue_style( 'wp-block-library' ); } } @@ -3245,6 +3291,10 @@ p { Jetpack_Options::update_options( compact( 'version', 'old_version' ) ); } + if ( self::is_connection_ready() ) { + self::handle_default_module_activation( true ); + } + self::load_modules(); Jetpack_Options::delete_option( 'do_activate' ); @@ -3252,98 +3302,129 @@ p { } /** + * Handles the activation of the default modules depending on the current state of the site: + * - If the site already has the jetpack_active_modules option, activate those. + * - If the site has a site-only connection, only activate the default modules that require only a site connection. + * - If the site has a user connection, activate the default modules that require a user connection. + * + * @param bool $should_activate_user_modules Whether the status of the user connection should be checked and the default modules that + * require a user connection activated. + */ + private static function handle_default_module_activation( $should_activate_user_modules ) { + $active_modules = Jetpack_Options::get_option( 'active_modules' ); + if ( $active_modules ) { + self::delete_active_modules(); + + // If there was previously activated modules (a reconnection), re-activate them all including those that require a user, and do not re-activate those that have been deactivated. + self::activate_default_modules( 999, 1, $active_modules, false ); + } else { + // Check for a user connection. + if ( $should_activate_user_modules && ( new Connection_Manager() )->get_connection_owner_id() ) { + self::activate_default_modules( false, false, array(), false, null, null, null ); + Jetpack_Options::update_option( 'active_modules_initialized', true ); + } else { + self::activate_default_modules( false, false, array(), false, null, null, false ); + } + } + } + + /** * Removes all connection options * * @static */ public static function plugin_deactivation() { require_once ABSPATH . '/wp-admin/includes/plugin.php'; + $tracking = new Tracking(); + $tracking->record_user_event( 'deactivate_plugin', array() ); if ( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) { Jetpack_Network::init()->deactivate(); } else { - self::disconnect( false ); - // Jetpack_Heartbeat::init()->deactivate(); + add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' ); + self::disconnect(); + Jetpack_Options::delete_option( 'version' ); } } /** + * Set activated option to 4 on jetpack_idc_disconnect action. + */ + public static function on_idc_disconnect() { + \Jetpack_Options::update_option( 'activated', 4 ); + } + + /** * Disconnects from the Jetpack servers. * Forgets all connection details and tells the Jetpack servers to do the same. * * @static */ - public static function disconnect( $update_activated_state = true ) { - wp_clear_scheduled_hook( 'jetpack_clean_nonces' ); + public static function disconnect() { + $connection = self::connection(); - $connection->clean_nonces( true ); // If the site is in an IDC because sync is not allowed, // let's make sure to not disconnect the production site. - if ( ! self::validate_sync_error_idc_option() ) { - $tracking = new Tracking(); - $tracking->record_user_event( 'disconnect_site', array() ); + $connection->disconnect_site( ! Identity_Crisis::validate_sync_error_idc_option() ); + } - $connection->disconnect_site_wpcom(); - } + /** + * Happens after a successfull disconnection. + * + * @static + */ + public static function jetpack_site_disconnected() { + Identity_Crisis::clear_all_idc_options(); - $connection->delete_all_connection_tokens(); - Jetpack_IDC::clear_all_idc_options(); + // Delete all the sync related data. Since it could be taking up space. + Sender::get_instance()->uninstall(); + + /** + * Filters whether the Jetpack activated state should be updated after disconnecting. + * + * @since 10.0.0 + * + * @param bool $update_activated_state Whether activated state should be updated after disconnecting, defaults to true. + */ + $update_activated_state = apply_filters( 'jetpack_update_activated_state_on_disconnect', true ); if ( $update_activated_state ) { Jetpack_Options::update_option( 'activated', 4 ); } - - if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) { - // Check then record unique disconnection if site has never been disconnected previously - if ( - 1 == $jetpack_unique_connection['disconnected'] ) { - $jetpack_unique_connection['disconnected'] = 1; - } else { - if ( 0 == $jetpack_unique_connection['disconnected'] ) { - // track unique disconnect - $jetpack = self::init(); - - $jetpack->stat( 'connections', 'unique-disconnect' ); - $jetpack->do_stats( 'server_side' ); - } - // increment number of times disconnected - $jetpack_unique_connection['disconnected'] += 1; - } - - Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection ); - } - - // Delete all the sync related data. Since it could be taking up space. - Sender::get_instance()->uninstall(); - - // Disable the Heartbeat cron - Jetpack_Heartbeat::init()->deactivate(); } /** - * Unlinks the current user from the linked WordPress.com user. - * - * @deprecated since 7.7 - * @see Automattic\Jetpack\Connection\Manager::disconnect_user() + * Disconnects the user * - * @param Integer $user_id the user identifier. - * @return Boolean Whether the disconnection of the user was successful. + * @param int $user_id The user ID to disconnect. */ - public static function unlink_user( $user_id = null ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::disconnect_user' ); - return Connection_Manager::disconnect_user( $user_id ); + public function disconnect_user( $user_id ) { + $this->connection_manager->disconnect_user( $user_id ); } /** * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load() + * + * @deprecated since Jetpack 9.7.0 + * @see Automattic\Jetpack\Connection\Manager::try_registration() + * + * @return bool|WP_Error */ public static function try_registration() { - $terms_of_service = new Terms_Of_Service(); - // The user has agreed to the TOS at some point by now. - $terms_of_service->agree(); + _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' ); + return static::connection()->try_registration(); + } - // Let's get some testing in beta versions and such. - if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) { - // Before attempting to connect, let's make sure that the domains are viable. + /** + * Checking the domain names in beta versions. + * If this is a development version, before attempting to connect, let's make sure that the domains are viable. + * + * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine. + * + * @return null|\WP_Error The domain validation error, or `null` if everything's fine. + */ + public static function registration_check_domains( $error ) { + if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) { $domains_to_check = array_unique( array( 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ), @@ -3351,33 +3432,29 @@ p { ) ); foreach ( $domains_to_check as $domain ) { - $result = self::connection()->is_usable_domain( $domain ); + $result = static::connection()->is_usable_domain( $domain ); if ( is_wp_error( $result ) ) { return $result; } } } - $result = self::register(); - - // If there was an error with registration and the site was not registered, record this so we can show a message. - if ( ! $result || is_wp_error( $result ) ) { - return $result; - } else { - return true; - } + return $error; } /** * Tracking an internal event log. Try not to put too much chaff in here. * * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA) + * + * @param mixed $code Error code to log. + * @param mixed $data Data to log. */ public static function log( $code, $data = null ) { - // only grab the latest 200 entries + // only grab the latest 200 entries. $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 ); - // Append our event to the log + // Append our event to the log. $log_entry = array( 'time' => time(), 'user_id' => get_current_user_id(), @@ -3391,7 +3468,7 @@ p { $log[] = $log_entry; // Try add_option first, to make sure it's not autoloaded. - // @todo: Add an add_option method to Jetpack_Options + // @todo: Add an add_option method to Jetpack_Options. if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) { Jetpack_Options::update_option( 'log', $log ); } @@ -3417,8 +3494,8 @@ p { /** * Get the internal event log. * - * @param $event (string) - only return the specific log events - * @param $num (int) - get specific number of latest results, limited to 200 + * @param string $event only return the specific log events. + * @param int $num - get specific number of latest results, limited to 200. * * @return array of log events || WP_Error for invalid params */ @@ -3433,7 +3510,7 @@ p { $entire_log = Jetpack_Options::get_option( 'log', array() ); - // If nothing set - act as it did before, otherwise let's start customizing the output + // If nothing set - act as it did before, otherwise let's start customizing the output. if ( ! $num && ! $event ) { return $entire_log; } else { @@ -3461,6 +3538,10 @@ p { /** * Log modification of important settings. + * + * @param string $option Option name. + * @param string $old_value Old value of option. + * @param string $value New value of option. */ public static function log_settings_change( $option, $old_value, $value ) { switch ( $option ) { @@ -3471,7 +3552,12 @@ p { } /** - * Return stat data for WPCOM sync + * Return stat data for WPCOM sync. + * + * @param bool $encode JSON encode the result. + * @param bool $extended Adds additional stats data. + * + * @return array|string Stats data. Array if $encode is false. JSON-encoded string is $encode is true. */ public static function get_stat_data( $encode = true, $extended = true ) { $data = Jetpack_Heartbeat::generate_stats_array(); @@ -3490,6 +3576,10 @@ p { /** * Get additional stat data to sync to WPCOM + * + * @param string $prefix Stats prefix. + * + * @return array stats values. */ public static function get_additional_stat_data( $prefix = '' ) { $return[ "{$prefix}themes" ] = self::get_parsed_theme_data(); @@ -3503,6 +3593,11 @@ p { return $return; } + /** + * Get current site's user count. + * + * @return int|string|null Number of users on the site. -1 for a large network. + */ private static function get_site_user_count() { global $wpdb; @@ -3512,7 +3607,7 @@ p { } } if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) { - // It wasn't there, so regenerate the data and save the transient + // It wasn't there, so regenerate the data and save the transient. $user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" ); set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS ); } @@ -3521,43 +3616,60 @@ p { /* Admin Pages */ + /** + * Admin init function. + * + * Runs on admin_init hook. + * + * @return void + */ function admin_init() { // If the plugin is not connected, display a connect message. if ( - // the plugin was auto-activated and needs its candy + // the plugin was auto-activated and needs its candy. Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' ) || - // the plugin is active, but was never activated. Probably came from a site-wide network activation + // the plugin is active, but was never activated. Probably came from a site-wide network activation. ! Jetpack_Options::get_option( 'activated' ) ) { self::plugin_initialize(); } - $is_development_mode = ( new Status() )->is_development_mode(); - if ( ! self::is_active() && ! $is_development_mode ) { + $is_offline_mode = ( new Status() )->is_offline_mode(); + $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ); + /** Already documented in automattic/jetpack-connection::src/class-client.php */ + $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false ); + + if ( ! $is_offline_mode ) { Jetpack_Connection_Banner::init(); - } elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) { + } + + if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) { // Upgrade: 1.1 -> 1.1.1 - // Check and see if host can verify the Jetpack servers' SSL certificate - $args = array(); - $connection = self::connection(); - Client::_wp_remote_request( - Connection_Utils::fix_url_for_bad_hosts( $connection->api_url( 'test' ) ), - $args, - true - ); + // Check and see if host can verify the Jetpack servers' SSL certificate. + $args = array(); + Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true ); } - if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) { + Jetpack_Recommendations_Banner::init(); + + if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) { add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) ); } add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) ); + add_action( 'load-plugins.php', array( $this, 'plugins_page_init_jetpack_state' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) ); + + if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) { + add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' ); + } + add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) ); - if ( self::is_active() || $is_development_mode ) { - // Artificially throw errors in certain whitelisted cases during plugin activation + if ( self::is_connection_ready() || $is_offline_mode ) { + // Artificially throw errors in certain specific cases during plugin activation. add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) ); } @@ -3567,22 +3679,36 @@ p { add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) ); } + /** + * Adds body classes. + * + * @param string $admin_body_class Body classes. + * + * @return string + */ function admin_body_class( $admin_body_class = '' ) { $classes = explode( ' ', trim( $admin_body_class ) ); - $classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected'; + $classes[] = self::is_connection_ready() ? 'jetpack-connected' : 'jetpack-disconnected'; $admin_body_class = implode( ' ', array_unique( $classes ) ); return " $admin_body_class "; } + /** + * Adds Jetpack Page styles by appending class to the admin body class. + * + * @param string $admin_body_class Existing admin body class string. + * + * @return string + */ static function add_jetpack_pagestyles( $admin_body_class = '' ) { return $admin_body_class . ' jetpack-pagestyles '; } /** * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load. - * This function artificially throws errors for such cases (whitelisted). + * This function artificially throws errors for such cases (per a specific list). * * @param string $plugin The activated plugin. */ @@ -3593,7 +3719,7 @@ p { if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) { $throw = false; - // Try and make sure it really was the stats plugin + // Try and make sure it really was the stats plugin. if ( ! class_exists( 'ReflectionFunction' ) ) { if ( 'stats.php' == basename( $plugin ) ) { $throw = true; @@ -3606,15 +3732,37 @@ p { } if ( $throw ) { + /* translators: Plugin name to deactivate. */ trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old “%1$s” plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR ); } } } + /** + * Call to Jetpack::state on the load-plugins.php hook. + * In case the jetpackState cookie is populated, this call will read and re-set the cookie before HTTP headers are sent. + */ + public function plugins_page_init_jetpack_state() { + self::state( 'message' ); + } + + /** + * Adds the intercept action to the check_admin_referer hook. + * + * @return void + */ function intercept_plugin_error_scrape_init() { add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 ); } + /** + * Detect if conflicting plugin is being deactivated. + * + * @param string $action The nonce action. + * @param bool|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago. + * + * @return void + */ function intercept_plugin_error_scrape( $action, $result ) { if ( ! $result ) { return; @@ -3622,6 +3770,7 @@ p { foreach ( $this->plugins_to_deactivate as $deactivate_me ) { if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) { + /* translators: Plugin name to deactivate. */ self::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old “%1$s” plugin.', 'jetpack' ), $deactivate_me[1] ), false ); } } @@ -3650,6 +3799,10 @@ p { } // Require Jetpack authentication for the remote file upload AJAX requests. + if ( ! $this->connection_manager ) { + $this->connection_manager = new Connection_Manager(); + } + $this->connection_manager->require_jetpack_authentication(); // Register the remote file upload AJAX handlers. @@ -3675,12 +3828,12 @@ p { $response = $this->upload_handler( true ); break; default: - $response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 ); + $response = new WP_Error( 'unknown_handler', 'Unknown Handler', 400 ); break; } if ( ! $response ) { - $response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 ); + $response = new WP_Error( 'unknown_error', 'Unknown Error', 400 ); } if ( is_wp_error( $response ) ) { @@ -3710,40 +3863,40 @@ p { * the attachment file of the media item (gotten through of the post_id) * will be updated instead of add a new one. * - * @param boolean $update_media_item - update media attachment - * @return array - An array describing the uploadind files process + * @param boolean $update_media_item - update media attachment. + * @return array - An array describing the uploadind files process. */ function upload_handler( $update_media_item = false ) { if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { - return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 ); + return new WP_Error( 405, get_status_header_desc( 405 ), 405 ); } $user = wp_authenticate( '', '' ); if ( ! $user || is_wp_error( $user ) ) { - return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 ); + return new WP_Error( 403, get_status_header_desc( 403 ), 403 ); } wp_set_current_user( $user->ID ); if ( ! current_user_can( 'upload_files' ) ) { - return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 ); + return new WP_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 ); } if ( empty( $_FILES ) ) { - return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 ); + return new WP_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 ); } foreach ( array_keys( $_FILES ) as $files_key ) { if ( ! isset( $_POST[ "_jetpack_file_hmac_{$files_key}" ] ) ) { - return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 ); + return new WP_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 ); } } $media_keys = array_keys( $_FILES['media'] ); - $token = Jetpack_Data::get_access_token( get_current_user_id() ); + $token = ( new Tokens() )->get_access_token( get_current_user_id() ); if ( ! $token || is_wp_error( $token ) ) { - return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 ); + return new WP_Error( 'unknown_token', 'Unknown Jetpack token', 403 ); } $uploaded_files = array(); @@ -3774,7 +3927,7 @@ p { if ( $update_media_item ) { if ( ! isset( $post_id ) || $post_id === 0 ) { - return new Jetpack_Error( 'invalid_input', 'Media ID must be defined.', 400 ); + return new WP_Error( 'invalid_input', 'Media ID must be defined.', 400 ); } $media_array = $_FILES['media']; @@ -3830,12 +3983,6 @@ p { 'type' => $attachment->post_mime_type, 'meta' => wp_get_attachment_metadata( $attachment_id ), ); - // Zip files uploads are not supported unless they are done for installation purposed - // lets delete them in case something goes wrong in this whole process - if ( 'application/zip' === $attachment->post_mime_type ) { - // Schedule a cleanup for 2 hours from now in case of failed install. - wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) ); - } } } if ( ! is_null( $global_post ) ) { @@ -3849,31 +3996,31 @@ p { * Add help to the Jetpack page * * @since Jetpack (1.2.3) - * @return false if not the Jetpack page + * @return void */ function admin_help() { $current_screen = get_current_screen(); - // Overview + // Overview. $current_screen->add_help_tab( array( 'id' => 'home', 'title' => __( 'Home', 'jetpack' ), 'content' => - '<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' . + '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' . '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' . '<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>', ) ); - // Screen Content + // Screen Content. if ( current_user_can( 'manage_options' ) ) { $current_screen->add_help_tab( array( 'id' => 'settings', 'title' => __( 'Settings', 'jetpack' ), 'content' => - '<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' . + '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' . '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' . '<ol>' . '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' . @@ -3884,23 +4031,44 @@ p { ); } - // Help Sidebar + // Help Sidebar. + $support_url = Redirect::get_url( 'jetpack-support' ); + $faq_url = Redirect::get_url( 'jetpack-faq' ); $current_screen->set_help_sidebar( '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' . - '<p><a href="https://jetpack.com/faq/" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' . - '<p><a href="https://jetpack.com/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' . + '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' . + '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' . '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>' ); } + /** + * Enqueues the jetpack-icons style. + * + * @return void + */ function admin_menu_css() { wp_enqueue_style( 'jetpack-icons' ); } + /** + * Returns true. + * + * @todo This is seemingly unused. + * + * @return bool + */ function admin_menu_order() { return true; } + /** + * Sorts the order of wp-admin menu items. + * + * @param array $menu_order Existing menu order. + * + * @return array + */ function jetpack_menu_order( $menu_order ) { $jp_menu_order = array(); @@ -3917,6 +4085,11 @@ p { return $jp_menu_order; } + /** + * Registers/enqueues Jetpack banner styles. + * + * @return void + */ function admin_banner_styles() { $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; @@ -3924,7 +4097,7 @@ p { wp_register_style( 'jetpack-dops-style', plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ), - array(), + array(), // Load styles for components so the modal can be used. JETPACK__VERSION ); } @@ -3939,15 +4112,23 @@ p { wp_style_add_data( 'jetpack', 'suffix', $min ); } + /** + * Add action links for the Jetpack plugin. + * + * @param array $actions Plugin actions. + * + * @return array + */ function plugin_action_links( $actions ) { - $jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), 'Jetpack' ) ); + $jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), __( 'My Jetpack', 'jetpack' ) ) ); + $support_link = ( new Host() )->is_woa_site() ? 'https://wordpress.com/help/contact/' : self::admin_url( 'page=jetpack-debugger' ); - if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_active() || ( new Status() )->is_development_mode() ) ) { + if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) { return array_merge( $jetpack_home, array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ), - array( 'support' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack-debugger ' ), __( 'Support', 'jetpack' ) ) ), + array( 'support' => sprintf( '<a href="%s">%s</a>', $support_link, __( 'Support', 'jetpack' ) ) ), $actions ); } @@ -3955,6 +4136,95 @@ p { return array_merge( $jetpack_home, $actions ); } + /** + * Adds the deactivation warning modal for Jetpack. + * + * @param string $hook The current admin page. + * + * @return void + */ + public function deactivate_dialog( $hook ) { + if ( + 'plugins.php' === $hook + && self::is_connection_ready() + ) { + + // Register jp-tracks-functions dependency. + Tracking::register_tracks_functions_scripts( true ); + + // add a deactivation script that will pick up deactivation actions for the Jetpack plugin. + Assets::register_script( + 'jetpack-plugins-page-js', + '_inc/build/plugins-page.js', + JETPACK__PLUGIN_FILE, + array( + 'in_footer' => true, + 'textdomain' => 'jetpack', + 'dependencies' => array( + 'wp-polyfill', + 'wp-components', + ), + ) + ); + Assets::enqueue_script( 'jetpack-plugins-page-js' ); + + // Add objects to be passed to the initial state of the app. + // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280. + wp_add_inline_script( 'jetpack-plugins-page-js', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( Jetpack_Redux_State_Helper::get_initial_state() ) ) . '"));', 'before' ); + + add_action( 'admin_footer', array( $this, 'jetpack_plugin_portal_containers' ) ); + } + } + + /** + * Outputs the wrapper for the plugin deactivation modal + * Contents are loaded by React script + * + * @return void + */ + public function jetpack_plugin_portal_containers() { + $this->load_view( 'admin/jetpack-plugin-portal-containers.php' ); + } + + /** + * Filters the login URL to include the registration flow in case the user isn't logged in. + * + * @param string $login_url The wp-login URL. + * @param string $redirect URL to redirect users after logging in. + * @since Jetpack 8.4 + * @return string + */ + public function login_url( $login_url, $redirect ) { + parse_str( (string) wp_parse_url( $redirect, PHP_URL_QUERY ), $redirect_parts ); + if ( ! empty( $redirect_parts[ self::$jetpack_redirect_login ] ) ) { + $login_url = add_query_arg( self::$jetpack_redirect_login, 'true', $login_url ); + } + return $login_url; + } + + /** + * Redirects non-authenticated users to authenticate with Calypso if redirect flag is set. + * + * @since Jetpack 8.4 + */ + public function login_init() { + // phpcs:ignore WordPress.Security.NonceVerification + if ( ! empty( $_GET[ self::$jetpack_redirect_login ] ) ) { + add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) ); + wp_safe_redirect( + add_query_arg( + array( + 'forceInstall' => 1, + 'url' => rawurlencode( get_site_url() ), + ), + // @todo provide way to go to specific calypso env. + self::get_calypso_host() . 'jetpack/connect' + ) + ); + exit; + } + } + /* * Registration flow: * 1 - ::admin_page_load() action=register @@ -3997,15 +4267,15 @@ p { add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ), 20 ); if ( ! empty( $_GET['jetpack_restate'] ) ) { - // Should only be used in intermediate redirects to preserve state across redirects + // Should only be used in intermediate redirects to preserve state across redirects. self::restate(); } if ( isset( $_GET['connect_url_redirect'] ) ) { - // @todo: Add validation against a known whitelist + // @todo: Add validation against a known allowed list. $from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe'; - // User clicked in the iframe to link their accounts - if ( ! self::is_user_connected() ) { + // User clicked in the iframe to link their accounts. + if ( ! self::connection()->is_user_connected() ) { $redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false; add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) ); @@ -4033,15 +4303,54 @@ p { if ( isset( $_GET['action'] ) ) { switch ( $_GET['action'] ) { - case 'authorize': - if ( self::is_active() && self::is_user_connected() ) { - self::state( 'message', 'already_authorized' ); - wp_safe_redirect( self::admin_url() ); + case 'authorize_redirect': + self::log( 'authorize_redirect' ); + + add_filter( + 'allowed_redirect_hosts', + function ( $domains ) { + $domains[] = 'jetpack.com'; + $domains[] = 'jetpack.wordpress.com'; + $domains[] = 'wordpress.com'; + $domains[] = wp_parse_url( static::get_calypso_host(), PHP_URL_HOST ); // May differ from `wordpress.com`. + return array_unique( $domains ); + } + ); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $dest_url = empty( $_GET['dest_url'] ) ? null : $_GET['dest_url']; + + if ( ! $dest_url || ( 0 === stripos( $dest_url, 'https://jetpack.com/' ) && 0 === stripos( $dest_url, 'https://wordpress.com/' ) ) ) { + // The destination URL is missing or invalid, nothing to do here. exit; } - self::log( 'authorize' ); - $client_server = new Jetpack_Client_Server(); - $client_server->client_authorize(); + + if ( static::connection()->is_connected() && static::connection()->is_user_connected() ) { + // The user is either already connected, or finished the connection process. + wp_safe_redirect( $dest_url ); + exit; + } elseif ( ! empty( $_GET['done'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + // The user decided not to proceed with setting up the connection. + wp_safe_redirect( self::admin_url( 'page=jetpack' ) ); + exit; + } + + $redirect_args = array( + 'page' => 'jetpack', + 'action' => 'authorize_redirect', + 'dest_url' => rawurlencode( $dest_url ), + 'done' => '1', + ); + + if ( ! empty( $_GET['from'] ) && 'jetpack_site_only_checkout' === $_GET['from'] ) { + $redirect_args['from'] = 'jetpack_site_only_checkout'; + } + + wp_safe_redirect( static::build_authorize_url( self::admin_url( $redirect_args ) ) ); + exit; + case 'authorize': + _doing_it_wrong( __METHOD__, 'The `page=jetpack&action=authorize` webhook is deprecated. Use `handler=jetpack-connection-webhooks&action=authorize` instead', 'Jetpack 9.5.0' ); + ( new Connection_Webhooks( $this->connection_manager ) )->handle_authorize(); exit; case 'register': if ( ! current_user_can( 'jetpack_connect' ) ) { @@ -4051,7 +4360,11 @@ p { check_admin_referer( 'jetpack-register' ); self::log( 'register' ); self::maybe_set_version_option(); - $registered = self::try_registration(); + $from = isset( $_GET['from'] ) ? $_GET['from'] : false; + if ( $from ) { + static::connection()->add_register_request_param( 'from', (string) $from ); + } + $registered = static::connection()->try_registration(); if ( is_wp_error( $registered ) ) { $error = $registered->get_error_code(); self::state( 'error', $error ); @@ -4069,7 +4382,6 @@ p { break; } - $from = isset( $_GET['from'] ) ? $_GET['from'] : false; $redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false; /** @@ -4103,6 +4415,7 @@ p { check_admin_referer( "jetpack_activate-$module" ); self::log( 'activate', $module ); if ( ! self::activate_module( $module ) ) { + /* translators: module/feature name */ self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) ); } // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end. @@ -4137,7 +4450,7 @@ p { check_admin_referer( 'jetpack-reconnect' ); self::log( 'reconnect' ); - $this->disconnect(); + self::disconnect(); wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) ); exit; case 'deactivate': @@ -4160,7 +4473,7 @@ p { $redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : ''; check_admin_referer( 'jetpack-unlink' ); self::log( 'unlink' ); - Connection_Manager::disconnect_user(); + $this->connection_manager->disconnect_user(); self::state( 'message', 'unlinked' ); if ( 'sub-unlink' == $redirect ) { wp_safe_redirect( admin_url() ); @@ -4212,7 +4525,9 @@ p { switch ( $message_code ) { case 'jetpack-manage': - $this->message = '<strong>' . sprintf( __( 'You are all set! Your site can now be managed from <a href="%s" target="_blank">wordpress.com/sites</a>.', 'jetpack' ), 'https://wordpress.com/sites' ) . '</strong>'; + $sites_url = esc_url( Redirect::get_url( 'calypso-sites' ) ); + // translators: %s is the URL to the "Sites" panel on wordpress.com. + $this->message = '<strong>' . sprintf( __( 'You are all set! Your site can now be managed from <a href="%s" target="_blank">wordpress.com/sites</a>.', 'jetpack' ), $sites_url ) . '</strong>'; if ( $activated_manage ) { $this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack' ) . '</strong>'; } @@ -4268,6 +4583,11 @@ p { add_filter( 'jetpack_short_module_description', 'wptexturize' ); } + /** + * Display admin notice upon error. + * + * @return void + */ function admin_notices() { if ( $this->error ) { @@ -4360,6 +4680,7 @@ p { echo wp_kses( sprintf( + /* translators: URL to deactivate Jetpack features. */ _nx( 'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.', 'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.', @@ -4424,152 +4745,72 @@ endif; } /** + * Initialize the jetpack stats instance only when needed + * + * @return void + */ + private function initialize_stats() { + if ( is_null( $this->a8c_mc_stats_instance ) ) { + $this->a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats(); + } + } + + /** * Record a stat for later output. This will only currently output in the admin_footer. + * + * @param string $group Stats group. + * @param string $detail Stats detail. */ function stat( $group, $detail ) { - if ( ! isset( $this->stats[ $group ] ) ) { - $this->stats[ $group ] = array(); - } - $this->stats[ $group ][] = $detail; + $this->initialize_stats(); + $this->a8c_mc_stats_instance->add( $group, $detail ); + + // Keep a local copy for backward compatibility (there are some direct checks on this). + $this->stats = $this->a8c_mc_stats_instance->get_current_stats(); } /** * Load stats pixels. $group is auto-prefixed with "x_jetpack-" + * + * @param string $method Used to check if method is "server-side". */ function do_stats( $method = '' ) { - if ( is_array( $this->stats ) && count( $this->stats ) ) { - foreach ( $this->stats as $group => $stats ) { - if ( is_array( $stats ) && count( $stats ) ) { - $args = array( "x_jetpack-{$group}" => implode( ',', $stats ) ); - if ( 'server_side' === $method ) { - self::do_server_side_stat( $args ); - } else { - echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />'; - } - } - unset( $this->stats[ $group ] ); - } + $this->initialize_stats(); + if ( 'server_side' === $method ) { + $this->a8c_mc_stats_instance->do_server_side_stats(); + } else { + $this->a8c_mc_stats_instance->do_stats(); } + + // Keep a local copy for backward compatibility (there are some direct checks on this). + $this->stats = array(); } /** * Runs stats code for a one-off, server-side. * - * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store. + * @param array|string $args The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store. * * @return bool If it worked. */ static function do_server_side_stat( $args ) { - $response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) ); - if ( is_wp_error( $response ) ) { - return false; - } - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - return false; - } - - return true; + $url = self::build_stats_url( $args ); + $a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats(); + return $a8c_mc_stats_instance->do_server_side_stat( $url ); } /** * Builds the stats url. * - * @param $args array|string The arguments to append to the URL. + * @param array|string $args The arguments to append to the URL. * * @return string The URL to be pinged. */ static function build_stats_url( $args ) { - $defaults = array( - 'v' => 'wpcom2', - 'rand' => md5( mt_rand( 0, 999 ) . time() ), - ); - $args = wp_parse_args( $args, $defaults ); - /** - * Filter the URL used as the Stats tracking pixel. - * - * @since 2.3.2 - * - * @param string $url Base URL used as the Stats tracking pixel. - */ - $base_url = apply_filters( - 'jetpack_stats_base_url', - 'https://pixel.wp.com/g.gif' - ); - $url = add_query_arg( $args, $base_url ); - return $url; - } - /** - * Get the role of the current user. - * - * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_current_user_to_role() instead. - * - * @access public - * @static - * - * @return string|boolean Current user's role, false if not enough capabilities for any of the roles. - */ - public static function translate_current_user_to_role() { - _deprecated_function( __METHOD__, 'jetpack-7.6.0' ); + $a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats(); + return $a8c_mc_stats_instance->build_stats_url( $args ); - $roles = new Roles(); - return $roles->translate_current_user_to_role(); - } - - /** - * Get the role of a particular user. - * - * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_user_to_role() instead. - * - * @access public - * @static - * - * @param \WP_User $user User object. - * @return string|boolean User's role, false if not enough capabilities for any of the roles. - */ - public static function translate_user_to_role( $user ) { - _deprecated_function( __METHOD__, 'jetpack-7.6.0' ); - - $roles = new Roles(); - return $roles->translate_user_to_role( $user ); - } - - /** - * Get the minimum capability for a role. - * - * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_role_to_cap() instead. - * - * @access public - * @static - * - * @param string $role Role name. - * @return string|boolean Capability, false if role isn't mapped to any capabilities. - */ - public static function translate_role_to_cap( $role ) { - _deprecated_function( __METHOD__, 'jetpack-7.6.0' ); - - $roles = new Roles(); - return $roles->translate_role_to_cap( $role ); - } - - /** - * Sign a user role with the master access token. - * If not specified, will default to the current user. - * - * @deprecated since 7.7 - * @see Automattic\Jetpack\Connection\Manager::sign_role() - * - * @access public - * @static - * - * @param string $role User role. - * @param int $user_id ID of the user. - * @return string Signed user role. - */ - public static function sign_role( $role, $user_id = null ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::sign_role' ); - return self::connection()->sign_role( $role, $user_id ); } /** @@ -4581,13 +4822,13 @@ endif; * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection. * If string, will be a custom redirect. * @param bool|string $from If not false, adds 'from=$from' param to the connect URL. - * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0 + * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0. * * @return string Connect URL */ function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) { $site_id = Jetpack_Options::get_option( 'id' ); - $blog_token = Jetpack_Data::get_access_token(); + $blog_token = ( new Tokens() )->get_access_token(); if ( $register || ! $blog_token || ! $site_id ) { $url = self::nonce_url_no_esc( self::admin_url( 'action=register' ), 'jetpack-register' ); @@ -4613,7 +4854,7 @@ endif; // Let's check the existing blog token to see if we need to re-register. We only check once per minute // because otherwise this logic can get us in to a loop. - $last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) ); + $last_connect_url_check = (int) Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ); if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) { Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() ); @@ -4624,7 +4865,7 @@ endif; if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - // Generating a register URL instead to refresh the existing token + // Generating a register URL instead to refresh the existing token. return $this->build_connect_url( $raw, $redirect, $from, true ); } } @@ -4648,14 +4889,23 @@ endif; return apply_filters( 'jetpack_build_connection_url', $url, $raw ); } + /** + * Create the Jetpack authorization URL. + * + * @param bool|string $redirect URL to redirect to. + * @param bool $iframe Whether to use the iframe version. + * + * @todo Update default value for redirect since the called function expects a string. + * + * @return mixed|void + */ public static function build_authorize_url( $redirect = false, $iframe = false ) { add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) ); add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) ); - add_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) ); if ( $iframe ) { - add_filter( 'jetpack_api_url', array( __CLASS__, 'filter_connect_api_iframe_url' ), 10, 2 ); + add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' ); } $c8n = self::connection(); @@ -4663,27 +4913,33 @@ endif; remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) ); remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) ); - remove_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) ); if ( $iframe ) { - remove_filter( 'jetpack_api_url', array( __CLASS__, 'filter_connect_api_iframe_url' ) ); + remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' ); } - return $url; + /** + * Filter the URL used when authorizing a user to a WordPress.com account. + * + * @since 8.9.0 + * + * @param string $url Connection URL. + */ + return apply_filters( 'jetpack_build_authorize_url', $url ); } /** * Filters the connection URL parameter array. * - * @param Array $args default URL parameters used by the package. - * @return Array the modified URL arguments array. + * @param array $args default URL parameters used by the package. + * @return array the modified URL arguments array. */ public static function filter_connect_request_body( $args ) { if ( Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) ) { - $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() ); + $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() ); $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug ) ? $gp_locale->slug : ''; @@ -4710,13 +4966,52 @@ endif; } /** + * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to + * add Jetpack-the-plugin related permissions. + * + * @since 10.0 + * + * @param array $current_user_connection_data An array containing the current user connection data. + * @return array + */ + public static function filter_jetpack_current_user_connection_data( $current_user_connection_data ) { + $jetpack_permissions = array( + 'admin_page' => current_user_can( 'jetpack_admin_page' ), + 'manage_modules' => current_user_can( 'jetpack_manage_modules' ), + 'network_admin' => current_user_can( 'jetpack_network_admin_page' ), + 'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ), + 'edit_posts' => current_user_can( 'edit_posts' ), + 'publish_posts' => current_user_can( 'publish_posts' ), + 'manage_options' => current_user_can( 'manage_options' ), + 'view_stats' => current_user_can( 'view_stats' ), + 'manage_plugins' => current_user_can( 'install_plugins' ) + && current_user_can( 'activate_plugins' ) + && current_user_can( 'update_plugins' ) + && current_user_can( 'delete_plugins' ), + ); + + if ( isset( $current_user_connection_data['permissions'] ) && + is_array( $current_user_connection_data['permissions'] ) ) { + $current_user_connection_data['permissions'] = array_merge( $current_user_connection_data['permissions'], $jetpack_permissions ); + } else { + $current_user_connection_data['permissions'] = $jetpack_permissions; + } + + return $current_user_connection_data; + } + + /** * Filters the URL that will process the connection data. It can be different from the URL * that we send the user to after everything is done. * * @param String $processing_url the default redirect URL used by the package. * @return String the modified URL. + * + * @deprecated since Jetpack 9.5.0 */ public static function filter_connect_processing_url( $processing_url ) { + _deprecated_function( __METHOD__, 'jetpack-9.5' ); + $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy. return $processing_url; } @@ -4742,26 +5037,6 @@ endif; } /** - * Filters the API URL that is used for connect requests. The method - * intercepts only the authorize URL and replaces it with another if needed. - * - * @param String $api_url the default redirect API URL used by the package. - * @param String $relative_url the path of the URL that's being used. - * @return String the modified URL. - */ - public static function filter_connect_api_iframe_url( $api_url, $relative_url ) { - - // Short-circuit on anything that is not related to connect requests. - if ( 'authorize' !== $relative_url ) { - return $api_url; - } - - $c8n = self::connection(); - - return $c8n->api_url( 'authorize_iframe' ); - } - - /** * This action fires at the beginning of the Manager::authorize method. */ public static function authorize_starting() { @@ -4790,6 +5065,25 @@ endif; } /** + * This action fires when the site is registered (connected at a site level). + */ + public function handle_unique_registrations_stats() { + $jetpack_unique_registrations = Jetpack_Options::get_option( 'unique_registrations' ); + // Checking if site has been registered previously before recording unique connection. + if ( ! $jetpack_unique_registrations ) { + + $jetpack_unique_registrations = 0; + + $this->stat( 'connections', 'unique-registrations' ); + $this->do_stats( 'server_side' ); + } + + // Increment number of times connected. + $jetpack_unique_registrations ++; + Jetpack_Options::update_option( 'unique_registrations', $jetpack_unique_registrations ); + } + + /** * This action fires at the end of the Manager::authorize method when a secondary user is * linked. */ @@ -4826,21 +5120,33 @@ endif; } /** - * Get our assumed site creation date. - * Calculated based on the earlier date of either: - * - Earliest admin user registration date. - * - Earliest date of post of any post type. - * - * @since 7.2.0 - * @deprecated since 7.8.0 - * - * @return string Assumed site creation date and time. + * Fires on the jetpack_site_registered hook and acitvates default modules + */ + public static function activate_default_modules_on_site_register() { + self::handle_default_module_activation( false ); + + // Since this is a fresh connection, be sure to clear out IDC options. + Identity_Crisis::clear_all_idc_options(); + } + + /** + * This action fires at the end of the REST_Connector connection_reconnect method when the + * reconnect process is completed. + * Note that this currently only happens when we don't need the user to re-authorize + * their WP.com account, eg in cases where we are restoring a connection with + * unhealthy blog token. */ - public static function get_assumed_site_creation_date() { - _deprecated_function( __METHOD__, 'jetpack-7.8', 'Automattic\\Jetpack\\Connection\\Manager' ); - return self::connection()->get_assumed_site_creation_date(); + public static function reconnection_completed() { + self::state( 'message', 'reconnection_completed' ); } + /** + * Apply activation source to a query string array. + * + * @param array $args Args used for a query string. + * + * @return void + */ public static function apply_activation_source_to_args( &$args ) { list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' ); @@ -4853,24 +5159,51 @@ endif; } } + /** + * Returns the reconnection URL. + * + * @param bool $raw True to return an unescaped URL. False returns value after `esc_url`. + * + * @return string|null + */ function build_reconnect_url( $raw = false ) { $url = wp_nonce_url( self::admin_url( 'action=reconnect' ), 'jetpack-reconnect' ); return $raw ? $url : esc_url( $url ); } + /** + * Jetpack Admin URL. + * + * @param array $args Query string args. + * + * @return string Jetpack admin URL. + */ public static function admin_url( $args = null ) { $args = wp_parse_args( $args, array( 'page' => 'jetpack' ) ); $url = add_query_arg( $args, admin_url( 'admin.php' ) ); return $url; } + /** + * Creates a nonce from an URL. + * + * @param string $actionurl URL for action. + * @param string $action Nonce action. + * @param string $name Query arg name. + * + * @return string + */ public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) { $actionurl = str_replace( '&', '&', $actionurl ); return add_query_arg( $name, wp_create_nonce( $action ), $actionurl ); } + /** + * Dismiss Jetpack notices. + * + * @return void + */ function dismiss_jetpack_notice() { - if ( ! isset( $_GET['jetpack-notice'] ) ) { return; } @@ -4887,6 +5220,14 @@ endif; } } + /** + * Determines which module has a higher sort order. + * + * @param array $a Modules array. + * @param array $b Modules array. + * + * @return int 0 if the same sort or (+/-) to indicate which is greater. + */ public static function sort_modules( $a, $b ) { if ( $a['sort'] == $b['sort'] ) { return 0; @@ -4895,6 +5236,13 @@ endif; return ( $a['sort'] < $b['sort'] ) ? -1 : 1; } + /** + * Recheck SSL status for use via an AJAX call. + * + * Sends data back via `wp_send_json`. + * + * @return void + */ function ajax_recheck_ssl() { check_ajax_referer( 'recheck-ssl', 'ajax-nonce' ); $result = self::permit_ssl( true ); @@ -4909,27 +5257,14 @@ endif; /* Client API */ /** - * Returns the requested Jetpack API URL + * Verify the onboarding token. * - * @deprecated since 7.7 - * @return string - */ - public static function api_url( $relative_url ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::api_url' ); - $connection = self::connection(); - return $connection->api_url( $relative_url ); - } - - /** - * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::fix_url_for_bad_hosts() instead. + * @param array $token_data Token data. + * @param string $token Token value. + * @param string $request_data JSON-encoded request data. * - * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets + * @return mixed */ - public static function fix_url_for_bad_hosts( $url ) { - _deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::fix_url_for_bad_hosts' ); - return Connection_Utils::fix_url_for_bad_hosts( $url ); - } - public static function verify_onboarding_token( $token_data, $token, $request_data ) { // Default to a blog token. $token_type = 'blog'; @@ -5002,20 +5337,23 @@ endif; /** * Validate an onboarding token for a specific action * + * @param string $token Onboarding token. + * @param string $action Action name. + * * @return boolean True if token/action pair is accepted, false if not */ public static function validate_onboarding_token_action( $token, $action ) { - // Compare tokens, bail if tokens do not match + // Compare tokens, bail if tokens do not match. if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) { return false; } - // List of valid actions we can take + // List of valid actions we can take. $valid_actions = array( '/jetpack/v4/settings', ); - // Whitelist the action + // Only allow valid actions. if ( ! in_array( $action, $valid_actions ) ) { return false; } @@ -5026,42 +5364,31 @@ endif; /** * Checks to see if the URL is using SSL to connect with Jetpack * - * @since 2.3.3 + * @param bool $force_recheck Force SSL recheck. + * * @return boolean + * @since 2.3.3 */ public static function permit_ssl( $force_recheck = false ) { - // Do some fancy tests to see if ssl is being supported + // Do some fancy tests to see if ssl is being supported. if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) { $message = ''; if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) { $ssl = 0; } else { - switch ( JETPACK_CLIENT__HTTPS ) { - case 'NEVER': - $ssl = 0; - $message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' ); - break; - case 'ALWAYS': - case 'AUTO': - default: - $ssl = 1; - break; - } + $ssl = 1; - // If it's not 'NEVER', test to see - if ( $ssl ) { - if ( ! wp_http_supports( array( 'ssl' => true ) ) ) { + if ( ! wp_http_supports( array( 'ssl' => true ) ) ) { + $ssl = 0; + $message = __( 'WordPress reports no SSL support', 'jetpack' ); + } else { + $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' ); + if ( is_wp_error( $response ) ) { $ssl = 0; $message = __( 'WordPress reports no SSL support', 'jetpack' ); - } else { - $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' ); - if ( is_wp_error( $response ) ) { - $ssl = 0; - $message = __( 'WordPress reports no SSL support', 'jetpack' ); - } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) { - $ssl = 0; - $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response ); - } + } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) { + $ssl = 0; + $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response ); } } } @@ -5072,8 +5399,8 @@ endif; return (bool) $ssl; } - /* - * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working. + /** + * Displays an admin_notice, alerting the user that outbound SSL isn't working. */ public function alert_auto_ssl_fail() { if ( ! current_user_can( 'manage_options' ) ) { @@ -5095,9 +5422,10 @@ endif; <p> <?php printf( + /* translators: Both are URLs. First for the connection debug tool and the second for a support page. */ __( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ), esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ), - esc_url( 'https://jetpack.com/support/getting-started-with-jetpack/troubleshooting-tips/' ) + esc_url( Redirect::get_url( 'jetpack-support-getting-started-troubleshooting-tips' ) ) ); ?> </p> @@ -5115,14 +5443,14 @@ endif; e.preventDefault(); var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' }; $.post( ajaxurl, data ) - .done( function( response ) { - if ( response.enabled ) { - $( '#jetpack-ssl-warning' ).hide(); - } else { - this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> ); - $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message ); - } - }.bind( $this ) ); + .done( function( response ) { + if ( response.enabled ) { + $( '#jetpack-ssl-warning' ).hide(); + } else { + this.html( <?php echo wp_json_encode( __( 'Try again', 'jetpack' ) ); ?> ); + $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message ); + } + }.bind( $this ) ); } ); } ); </script> @@ -5131,23 +5459,19 @@ endif; } /** - * Returns the Jetpack XML-RPC API - * - * @deprecated 8.0 Use Connection_Manager instead. - * @return string - */ - public static function xmlrpc_api_url() { - _deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_api_url()' ); - return self::connection()->xmlrpc_api_url(); - } - - /** * Returns the connection manager object. * * @return Automattic\Jetpack\Connection\Manager */ public static function connection() { - return self::init()->connection_manager; + $jetpack = static::init(); + + // If the connection manager hasn't been instantiated, do that now. + if ( ! $jetpack->connection_manager ) { + $jetpack->connection_manager = new Connection_Manager( 'jetpack' ); + } + + return $jetpack->connection_manager; } /** @@ -5155,6 +5479,8 @@ endif; * * Note these tokens are unique per call, NOT static per site for connecting. * + * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead. + * * @since 2.6 * @param String $action The action name. * @param Integer $user_id The user identifier. @@ -5162,17 +5488,26 @@ endif; * @return array */ public static function generate_secrets( $action, $user_id = false, $exp = 600 ) { + _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' ); return self::connection()->generate_secrets( $action, $user_id, $exp ); } + /** + * Get verification secrets. + * + * @param string $action Action name. + * @param int $user_id User ID. + * + * @return array|string|WP_Error + */ public static function get_secrets( $action, $user_id ) { - $secrets = self::connection()->get_secrets( $action, $user_id ); + $secrets = ( new Secrets() )->get( $action, $user_id ); - if ( Connection_Manager::SECRETS_MISSING === $secrets ) { + if ( Secrets::SECRETS_MISSING === $secrets ) { return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' ); } - if ( Connection_Manager::SECRETS_EXPIRED === $secrets ) { + if ( Secrets::SECRETS_EXPIRED === $secrets ) { return new WP_Error( 'verify_secrets_expired', 'Verification took too long' ); } @@ -5180,118 +5515,38 @@ endif; } /** - * @deprecated 7.5 Use Connection_Manager instead. + * Register a connection. * - * @param $action - * @param $user_id - */ - public static function delete_secrets( $action, $user_id ) { - return self::connection()->delete_secrets( $action, $user_id ); - } - - /** - * Builds the timeout limit for queries talking with the wpcom servers. + * @deprecated Jetpack 9.7.0 + * @see Automattic\Jetpack\Connection\Manager::try_registration() * - * Based on local php max_execution_time in php.ini - * - * @since 2.6 - * @return int - * @deprecated - **/ - public function get_remote_query_timeout_limit() { - _deprecated_function( __METHOD__, 'jetpack-5.4' ); - return self::get_max_execution_time(); - } - - /** - * Builds the timeout limit for queries talking with the wpcom servers. - * - * Based on local php max_execution_time in php.ini - * - * @since 5.4 - * @return int - **/ - public static function get_max_execution_time() { - $timeout = (int) ini_get( 'max_execution_time' ); - - // Ensure exec time set in php.ini - if ( ! $timeout ) { - $timeout = 30; - } - return $timeout; - } - - /** - * Sets a minimum request timeout, and returns the current timeout - * - * @since 5.4 - **/ - public static function set_min_time_limit( $min_timeout ) { - $timeout = self::get_max_execution_time(); - if ( $timeout < $min_timeout ) { - $timeout = $min_timeout; - set_time_limit( $timeout ); - } - return $timeout; - } - - /** - * Takes the response from the Jetpack register new site endpoint and - * verifies it worked properly. - * - * @since 2.6 - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::validate_remote_register_response() - **/ - public function validate_remote_register_response() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::validate_remote_register_response' ); - } - - /** * @return bool|WP_Error */ public static function register() { - $tracking = new Tracking(); - $tracking->record_user_event( 'jpc_register_begin' ); - - add_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) ); - - $connection = self::connection(); - $registration = $connection->register(); - - remove_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) ); - - if ( ! $registration || is_wp_error( $registration ) ) { - return $registration; - } - - return true; + _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' ); + return static::connection()->try_registration( false ); } /** * Filters the registration request body to include tracking properties. * - * @param Array $properties - * @return Array amended properties. + * @deprecated Jetpack 9.7.0 + * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body() + * + * @param array $properties Token request properties. + * @return array amended properties. */ public static function filter_register_request_body( $properties ) { - $tracking = new Tracking(); - $tracks_identity = $tracking->tracks_get_identity( get_current_user_id() ); - - return array_merge( - $properties, - array( - '_ui' => $tracks_identity['_ui'], - '_ut' => $tracks_identity['_ut'], - ) - ); + _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' ); + return Connection_Utils::filter_register_request_body( $properties ); } /** * Filters the token request body to include tracking properties. * - * @param Array $properties - * @return Array amended properties. + * @param array $properties + * + * @return array amended properties. */ public static function filter_token_request_body( $properties ) { $tracking = new Tracking(); @@ -5332,6 +5587,9 @@ endif; * Loads the Jetpack XML-RPC client. * No longer necessary, as the XML-RPC client will be automagically loaded. * + * Note: we cannot remove this function yet as it is used in this plugin: + * https://wordpress.org/plugins/jetpack-subscription-form/ + * * @deprecated since 7.7.0 */ public static function load_xml_rpc_client() { @@ -5339,234 +5597,13 @@ endif; } /** - * Resets the saved authentication state in between testing requests. - */ - public function reset_saved_auth_state() { - $this->rest_authentication_status = null; - $this->connection_manager->reset_saved_auth_state(); - } - - /** - * Verifies the signature of the current request. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::verify_xml_rpc_signature() - * - * @return false|array - */ - public function verify_xml_rpc_signature() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::verify_xml_rpc_signature' ); - return self::connection()->verify_xml_rpc_signature(); - } - - /** - * Verifies the signature of the current request. - * - * This function has side effects and should not be used. Instead, - * use the memoized version `->verify_xml_rpc_signature()`. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::internal_verify_xml_rpc_signature() - * @internal - */ - private function internal_verify_xml_rpc_signature() { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::internal_verify_xml_rpc_signature' ); - } - - /** - * Authenticates XML-RPC and other requests from the Jetpack Server. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::authenticate_jetpack() - * - * @param \WP_User|mixed $user User object if authenticated. - * @param string $username Username. - * @param string $password Password string. - * @return \WP_User|mixed Authenticated user or error. - */ - public function authenticate_jetpack( $user, $username, $password ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::authenticate_jetpack' ); - return $this->connection_manager->authenticate_jetpack( $user, $username, $password ); - } - - // Authenticates requests from Jetpack server to WP REST API endpoints. - // Uses the existing XMLRPC request signing implementation. - function wp_rest_authenticate( $user ) { - if ( ! empty( $user ) ) { - // Another authentication method is in effect. - return $user; - } - - if ( ! isset( $_GET['_for'] ) || $_GET['_for'] !== 'jetpack' ) { - // Nothing to do for this authentication method. - return null; - } - - if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) { - // Nothing to do for this authentication method. - return null; - } - - // Ensure that we always have the request body available. At this - // point, the WP REST API code to determine the request body has not - // run yet. That code may try to read from 'php://input' later, but - // this can only be done once per request in PHP versions prior to 5.6. - // So we will go ahead and perform this read now if needed, and save - // the request body where both the Jetpack signature verification code - // and the WP REST API code can see it. - if ( ! isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ) { - $GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents( 'php://input' ); - } - $this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA']; - - // Only support specific request parameters that have been tested and - // are known to work with signature verification. A different method - // can be passed to the WP REST API via the '?_method=' parameter if - // needed. - if ( $_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST' ) { - $this->rest_authentication_status = new WP_Error( - 'rest_invalid_request', - __( 'This request method is not supported.', 'jetpack' ), - array( 'status' => 400 ) - ); - return null; - } - if ( $_SERVER['REQUEST_METHOD'] !== 'POST' && ! empty( $this->HTTP_RAW_POST_DATA ) ) { - $this->rest_authentication_status = new WP_Error( - 'rest_invalid_request', - __( 'This request method does not support body parameters.', 'jetpack' ), - array( 'status' => 400 ) - ); - return null; - } - - $verified = $this->connection_manager->verify_xml_rpc_signature(); - - if ( - $verified && - isset( $verified['type'] ) && - 'user' === $verified['type'] && - ! empty( $verified['user_id'] ) - ) { - // Authentication successful. - $this->rest_authentication_status = true; - return $verified['user_id']; - } - - // Something else went wrong. Probably a signature error. - $this->rest_authentication_status = new WP_Error( - 'rest_invalid_signature', - __( 'The request is not signed correctly.', 'jetpack' ), - array( 'status' => 400 ) - ); - return null; - } - - /** - * Report authentication status to the WP REST API. - * - * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it, or another value if not - * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication} - */ - public function wp_rest_authentication_errors( $value ) { - if ( $value !== null ) { - return $value; - } - return $this->rest_authentication_status; - } - - /** - * Add our nonce to this request. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::add_nonce() - * - * @param int $timestamp Timestamp of the request. - * @param string $nonce Nonce string. - */ - public function add_nonce( $timestamp, $nonce ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::add_nonce' ); - return $this->connection_manager->add_nonce( $timestamp, $nonce ); - } - - /** - * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods. - * Capture it here so we can verify the signature later. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::xmlrpc_methods() - * - * @param array $methods XMLRPC methods. - * @return array XMLRPC methods, with the $HTTP_RAW_POST_DATA one. - */ - public function xmlrpc_methods( $methods ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_methods' ); - return $this->connection_manager->xmlrpc_methods( $methods ); - } - - /** - * Register additional public XMLRPC methods. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::public_xmlrpc_methods() - * - * @param array $methods Public XMLRPC methods. - * @return array Public XMLRPC methods, with the getOptions one. - */ - public function public_xmlrpc_methods( $methods ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::public_xmlrpc_methods' ); - return $this->connection_manager->public_xmlrpc_methods( $methods ); - } - - /** - * Handles a getOptions XMLRPC method call. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::jetpack_getOptions() - * - * @param array $args method call arguments. - * @return array an amended XMLRPC server options array. - */ - public function jetpack_getOptions( $args ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::jetpack_getOptions' ); - return $this->connection_manager->jetpack_getOptions( $args ); - } - - /** - * Adds Jetpack-specific options to the output of the XMLRPC options method. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::xmlrpc_options() - * - * @param array $options Standard Core options. - * @return array Amended options. - */ - public function xmlrpc_options( $options ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_options' ); - return $this->connection_manager->xmlrpc_options( $options ); - } - - /** - * Cleans nonces that were saved when calling ::add_nonce. - * - * @deprecated since 7.7.0 - * @see Automattic\Jetpack\Connection\Manager::clean_nonces() - * - * @param bool $all whether to clean even non-expired nonces. - */ - public static function clean_nonces( $all = false ) { - _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::clean_nonces' ); - return self::connection()->clean_nonces( $all ); - } - - /** * State is passed via cookies from one request to the next, but never to subsequent requests. * SET: state( $key, $value ); * GET: $value = state( $key ); * - * @param string $key - * @param string $value - * @param bool $restate private + * @param string $key State key. + * @param string $value Value. + * @param bool $restate Reset the cookie (private). */ public static function state( $key = null, $value = null, $restate = false ) { static $state = array(); @@ -5584,9 +5621,9 @@ endif; } } - // Extract state from cookies and delete cookies + // Extract state from cookies and delete cookies. if ( isset( $_COOKIE['jetpackState'] ) && is_array( $_COOKIE['jetpackState'] ) ) { - $yum = $_COOKIE['jetpackState']; + $yum = wp_unslash( $_COOKIE['jetpackState'] ); unset( $_COOKIE['jetpackState'] ); foreach ( $yum as $k => $v ) { if ( strlen( $v ) ) { @@ -5603,7 +5640,7 @@ endif; return; } - // Get a state variable + // Get a state variable. if ( isset( $key ) && ! isset( $value ) ) { if ( array_key_exists( $key, $state ) ) { return $state[ $key ]; @@ -5611,22 +5648,55 @@ endif; return null; } - // Set a state variable + // Set a state variable. if ( isset( $key ) && isset( $value ) ) { if ( is_array( $value ) && isset( $value[0] ) ) { $value = $value[0]; } $state[ $key ] = $value; if ( ! headers_sent() ) { - setcookie( "jetpackState[$key]", $value, 0, $path, $domain ); + if ( self::should_set_cookie( $key ) ) { + setcookie( "jetpackState[$key]", $value, 0, $path, $domain ); + } } } } + /** + * Set an empty state. + * + * @return void + */ public static function restate() { self::state( null, null, true ); } + /** + * Determines whether the jetpackState[$key] value should be added to the + * cookie. + * + * @param string $key The state key. + * + * @return boolean Whether the value should be added to the cookie. + */ + public static function should_set_cookie( $key ) { + global $current_screen; + $page = isset( $current_screen->base ) ? $current_screen->base : null; + + if ( 'toplevel_page_jetpack' === $page && 'display_update_modal' === $key ) { + return false; + } + + return true; + } + + /** + * Check if site is publicly accessible. + * + * @param string $file Module file. + * + * @return void + */ public static function check_privacy( $file ) { static $is_site_publicly_accessible = null; @@ -5663,84 +5733,15 @@ endif; } /** - * Helper method for multicall XMLRPC. + * Serve a WordPress.com static resource via a randomized wp.com subdomain. + * + * @deprecated 9.3.0 Use Assets::staticize_subdomain. * - * @param ...$args Args for the async_call. + * @param string $url WordPress.com static resource URL. */ - public static function xmlrpc_async_call( ...$args ) { - global $blog_id; - static $clients = array(); - - $client_blog_id = is_multisite() ? $blog_id : 0; - - if ( ! isset( $clients[ $client_blog_id ] ) ) { - $clients[ $client_blog_id ] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER ) ); - if ( function_exists( 'ignore_user_abort' ) ) { - ignore_user_abort( true ); - } - add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) ); - } - - if ( ! empty( $args[0] ) ) { - call_user_func_array( array( $clients[ $client_blog_id ], 'addCall' ), $args ); - } elseif ( is_multisite() ) { - foreach ( $clients as $client_blog_id => $client ) { - if ( ! $client_blog_id || empty( $client->calls ) ) { - continue; - } - - $switch_success = switch_to_blog( $client_blog_id, true ); - if ( ! $switch_success ) { - continue; - } - - flush(); - $client->query(); - - restore_current_blog(); - } - } else { - if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) { - flush(); - $clients[0]->query(); - } - } - } - public static function staticize_subdomain( $url ) { - - // Extract hostname from URL - $host = wp_parse_url( $url, PHP_URL_HOST ); - - // Explode hostname on '.' - $exploded_host = explode( '.', $host ); - - // Retrieve the name and TLD - if ( count( $exploded_host ) > 1 ) { - $name = $exploded_host[ count( $exploded_host ) - 2 ]; - $tld = $exploded_host[ count( $exploded_host ) - 1 ]; - // Rebuild domain excluding subdomains - $domain = $name . '.' . $tld; - } else { - $domain = $host; - } - // Array of Automattic domains - $domain_whitelist = array( 'wordpress.com', 'wp.com' ); - - // Return $url if not an Automattic domain - if ( ! in_array( $domain, $domain_whitelist ) ) { - return $url; - } - - if ( is_ssl() ) { - return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url ); - } - - srand( crc32( basename( $url ) ) ); - $static_counter = rand( 0, 2 ); - srand(); // this resets everything that relies on this, like array_rand() and shuffle() - - return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url ); + _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' ); + return Assets::staticize_subdomain( $url ); } /* JSON API Authorization */ @@ -5758,7 +5759,13 @@ endif; add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 ); } - // Make sure the login form is POSTed to the signed URL so we can reverify the request + /** + * Make sure the login form is POSTed to the signed URL so we can reverify the request. + * + * @param string $url Redirect URL. + * @param string $path Path. + * @param string $scheme URL Scheme. + */ function post_login_form_to_signed_url( $url, $path, $scheme ) { if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) { return $url; @@ -5774,13 +5781,20 @@ endif; return $url; } - // Make sure the POSTed request is handled by the same action + /** + * Make sure the POSTed request is handled by the same action. + */ function preserve_action_in_login_form_for_json_api_authorization() { echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n"; echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n"; } - // If someone logs in to approve API access, store the Access Code in usermeta + /** + * If someone logs in to approve API access, store the Access Code in usermeta. + * + * @param string $user_login Unused. + * @param WP_User $user User logged in. + */ function store_json_api_authorization_token( $user_login, $user ) { add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 ); add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) ); @@ -5788,17 +5802,36 @@ endif; update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token ); } - // Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access + /** + * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access. + * + * To be used with a filter of allowed domains for a redirect. + * + * @param array $domains Allowed WP.com Environments. + */ function allow_wpcom_public_api_domain( $domains ) { $domains[] = 'public-api.wordpress.com'; return $domains; } + /** + * Check if the redirect is encoded. + * + * @param string $redirect_url Redirect URL. + * + * @return bool If redirect has been encoded. + */ static function is_redirect_encoded( $redirect_url ) { return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0; } - // Add all wordpress.com environments to the safe redirect whitelist + /** + * Add all wordpress.com environments to the safe redirect allowed list. + * + * To be used with a filter of allowed domains for a redirect. + * + * @param array $domains Allowed WP.com Environments. + */ function allow_wpcom_environments( $domains ) { $domains[] = 'wordpress.com'; $domains[] = 'wpcalypso.wordpress.com'; @@ -5807,7 +5840,15 @@ endif; return $domains; } - // Add the Access Code details to the public-api.wordpress.com redirect + /** + * Add the Access Code details to the public-api.wordpress.com redirect. + * + * @param string $redirect_to URL. + * @param string $original_redirect_to URL. + * @param WP_User $user WP_User for the redirect. + * + * @return string + */ function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) { return add_query_arg( urlencode_deep( @@ -5821,29 +5862,28 @@ endif; ); } - /** * Verifies the request by checking the signature * * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO. * - * @param null|array $environment + * @param null|array $environment Value to override $_REQUEST. */ function verify_json_api_authorization_request( $environment = null ) { $environment = is_null( $environment ) ? $_REQUEST : $environment; - list( $envToken, $envVersion, $envUserId ) = explode( ':', $environment['token'] ); - $token = Jetpack_Data::get_access_token( $envUserId, $envToken ); + list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] ); + $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token ); if ( ! $token || empty( $token->secret ) ) { wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) ); } - $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' ); + $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' ); - // Host has encoded the request URL, probably as a result of a bad http => https redirect + // Host has encoded the request URL, probably as a result of a bad http => https redirect. if ( self::is_redirect_encoded( $_GET['redirect_to'] ) ) { /** * Jetpack authorisation request Error. @@ -5854,7 +5894,7 @@ endif; $die_error = sprintf( /* translators: %s is a URL */ __( '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' ), - 'https://jetpack.com/support/double-encoding/' + Redirect::get_url( 'jetpack-support-double-encoding' ) ); } @@ -5886,7 +5926,7 @@ endif; wp_die( $die_error ); } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) { if ( is_ssl() ) { - // 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 + // 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. $signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', @@ -5905,12 +5945,16 @@ endif; $timestamp = (int) $environment['timestamp']; $nonce = stripslashes( (string) $environment['nonce'] ); - if ( ! $this->connection_manager->add_nonce( $timestamp, $nonce ) ) { + if ( ! $this->connection_manager ) { + $this->connection_manager = new Connection_Manager(); + } + + if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) { // De-nonce the nonce, at least for 5 minutes. - // 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) + // 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). $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" ); if ( $old_nonce_time < time() - 300 ) { - wp_die( __( 'The authorization process expired. Please go back and try again.', 'jetpack' ) ); + wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) ); } } @@ -5948,9 +5992,17 @@ endif; } } + /** + * HTML for the JSON API authorization notice. + * + * @param string $message Authorization message. Unused. + * + * @return string + */ function login_message_json_api_authorization( $message ) { return '<p class="message">' . sprintf( - esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ), + /* translators: Name/image of the client requesting authorization */ + esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ), '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>' ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>'; } @@ -5975,14 +6027,14 @@ endif; /** * Pings the WordPress.com Mirror Site for the specified options. * - * @param string|array $option_names The option names to request from the WordPress.com Mirror Site + * @param string|array $option_names The option names to request from the WordPress.com Mirror Site. * * @return array An associative array of the option values as stored in the WordPress.com Mirror Site */ public function get_cloud_site_options( $option_names ) { $option_names = array_filter( (array) $option_names, 'is_string' ); - $xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER ) ); + $xml = new Jetpack_IXR_Client(); $xml->query( 'jetpack.fetchSiteOptions', $option_names ); if ( $xml->isError() ) { return array( @@ -6001,7 +6053,7 @@ endif; * @return array|bool Array of options that are in a crisis, or false if everything is OK. */ public static function check_identity_crisis() { - if ( ! self::is_active() || ( new Status() )->is_development_mode() || ! self::validate_sync_error_idc_option() ) { + if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) { return false; } @@ -6009,19 +6061,6 @@ endif; } /** - * Checks whether the home and siteurl specifically are whitelisted - * Written so that we don't have re-check $key and $value params every time - * we want to check if this site is whitelisted, for example in footer.php - * - * @since 3.8.0 - * @return bool True = already whitelisted False = not whitelisted - */ - public static function is_staging_site() { - _deprecated_function( 'Jetpack::is_staging_site', 'jetpack-8.1', '/Automattic/Jetpack/Status->is_staging_site' ); - return ( new Status() )->is_staging_site(); - } - - /** * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup. * * @since 4.4.0 @@ -6030,52 +6069,8 @@ endif; * @return bool */ public static function validate_sync_error_idc_option() { - $is_valid = false; - - $idc_allowed = get_transient( 'jetpack_idc_allowed' ); - if ( false === $idc_allowed ) { - $response = wp_remote_get( 'https://jetpack.com/is-idc-allowed/' ); - if ( 200 === (int) wp_remote_retrieve_response_code( $response ) ) { - $json = json_decode( wp_remote_retrieve_body( $response ) ); - $idc_allowed = isset( $json, $json->result ) && $json->result ? '1' : '0'; - $transient_duration = HOUR_IN_SECONDS; - } else { - // If the request failed for some reason, then assume IDC is allowed and set shorter transient. - $idc_allowed = '1'; - $transient_duration = 5 * MINUTE_IN_SECONDS; - } - - set_transient( 'jetpack_idc_allowed', $idc_allowed, $transient_duration ); - } - - // Is the site opted in and does the stored sync_error_idc option match what we now generate? - $sync_error = Jetpack_Options::get_option( 'sync_error_idc' ); - if ( $idc_allowed && $sync_error && self::sync_idc_optin() ) { - $local_options = self::get_sync_error_idc_option(); - // Ensure all values are set. - if ( isset( $sync_error['home'] ) && isset ( $local_options['home'] ) && isset( $sync_error['siteurl'] ) && isset( $local_options['siteurl'] ) ) { - if ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) { - $is_valid = true; - } - } - - } - - /** - * Filters whether the sync_error_idc option is valid. - * - * @since 4.4.0 - * - * @param bool $is_valid If the sync_error_idc is valid or not. - */ - $is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid ); - - if ( ! $idc_allowed || ( ! $is_valid && $sync_error ) ) { - // Since the option exists, and did not validate, delete it - Jetpack_Options::delete_option( 'sync_error_idc' ); - } - - return $is_valid; + _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' ); + return Identity_Crisis::validate_sync_error_idc_option(); } /** @@ -6085,16 +6080,17 @@ endif; * - Adds a trailing slash * * @since 4.4.0 - * @param string $url + * @param string $url URL. * @return WP_Error|string */ public static function normalize_url_protocol_agnostic( $url ) { $parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) ); if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) { + /* translators: URL string */ return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) ); } - // Strip www and protocols + // Strip www and protocols. $url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] ); return $url; } @@ -6104,41 +6100,14 @@ endif; * * @since 4.4.0 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB + * @deprecated 9.8.0 Use \\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option * - * @param array $response + * @param array $response HTTP response. * @return array Array of the local urls, wpcom urls, and error code */ public static function get_sync_error_idc_option( $response = array() ) { - // Since the local options will hit the database directly, store the values - // in a transient to allow for autoloading and caching on subsequent views. - $local_options = get_transient( 'jetpack_idc_local' ); - if ( false === $local_options ) { - $local_options = array( - 'home' => Functions::home_url(), - 'siteurl' => Functions::site_url(), - ); - set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS ); - } - - $options = array_merge( $local_options, $response ); - - $returned_values = array(); - foreach ( $options as $key => $option ) { - if ( 'error_code' === $key ) { - $returned_values[ $key ] = $option; - continue; - } - - if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) { - continue; - } - - $returned_values[ $key ] = $normalized_url; - } - - set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS ); - - return $returned_values; + _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' ); + return Identity_Crisis::get_sync_error_idc_option( $response ); } /** @@ -6149,28 +6118,20 @@ endif; * @return bool */ public static function sync_idc_optin() { - if ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) { - $default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' ); - } else { - $default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite(); - } - - /** - * Allows sites to optin to IDC mitigation which blocks the site from syncing to WordPress.com when the home - * URL or site URL do not match what WordPress.com expects. The default value is either false, or the value of - * JETPACK_SYNC_IDC_OPTIN constant if set. - * - * @since 4.3.2 - * - * @param bool $default Whether the site is opted in to IDC mitigation. - */ - return (bool) apply_filters( 'jetpack_sync_idc_optin', $default ); + _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' ); + return Identity_Crisis::sync_idc_optin(); } /** * Maybe Use a .min.css stylesheet, maybe not. * * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args. + * + * @param string $url URL. + * @param string $path File path. + * @param string $plugin Plugin. + * + * @return mixed */ public static function maybe_min_asset( $url, $path, $plugin ) { // Short out on things trying to find actual paths. @@ -6220,9 +6181,10 @@ endif; * * Attached to `style_loader_src` filter. * - * @param string $tag The tag that would link to the external asset. + * @param string $src source file. * @param string $handle The registered handle of the script in question. - * @param string $href The url of the asset in question. + * + * @return mixed */ public static function set_suffix_on_min( $src, $handle ) { if ( false === strpos( $src, '.min.css' ) ) { @@ -6264,11 +6226,11 @@ endif; if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) { $href = $matches[1]; - // Strip off query string + // Strip off query string. if ( $pos = strpos( $href, '?' ) ) { $href = substr( $href, 0, $pos ); } - // Strip off fragment + // Strip off fragment. if ( $pos = strpos( $href, '#' ) ) { $href = substr( $href, 0, $pos ); } @@ -6311,9 +6273,9 @@ endif; * Data passed in with the $data parameter will be available in the * template file as $data['value'] * - * @param string $template - Template file to load - * @param array $data - Any data to pass along to the template - * @return boolean - If template file was found + * @param string $template - Template file to load. + * @param array $data - Any data to pass along to the template. + * @return boolean - If template file was found. **/ public function load_view( $template, $data = array() ) { $views_dir = JETPACK__PLUGIN_DIR . 'views/'; @@ -6328,77 +6290,262 @@ endif; } /** - * Throws warnings for deprecated hooks to be removed from Jetpack + * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code. */ public function deprecated_hooks() { - global $wp_filter; + $filter_deprecated_list = array( + 'jetpack_bail_on_shortcode' => array( + 'replacement' => 'jetpack_shortcodes_to_include', + 'version' => 'jetpack-3.1.0', + ), + 'wpl_sharing_2014_1' => array( + 'replacement' => null, + 'version' => 'jetpack-3.6.0', + ), + 'jetpack-tools-to-include' => array( + 'replacement' => 'jetpack_tools_to_include', + 'version' => 'jetpack-3.9.0', + ), + 'jetpack_identity_crisis_options_to_check' => array( + 'replacement' => null, + 'version' => 'jetpack-4.0.0', + ), + 'update_option_jetpack_single_user_site' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'audio_player_default_colors' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_featured_images_enabled' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_update_details' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_updates' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_name' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_allow_new_registrations' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_add_new_users' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_site_upload_space' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_upload_file_types' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_network_enable_administration_menus' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_is_multi_site' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_is_main_network' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'add_option_jetpack_main_network_site' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'jetpack_sync_all_registered_options' => array( + 'replacement' => null, + 'version' => 'jetpack-4.3.0', + ), + 'jetpack_has_identity_crisis' => array( + 'replacement' => 'jetpack_sync_error_idc_validation', + 'version' => 'jetpack-4.4.0', + ), + 'jetpack_is_post_mailable' => array( + 'replacement' => null, + 'version' => 'jetpack-4.4.0', + ), + 'jetpack_seo_site_host' => array( + 'replacement' => null, + 'version' => 'jetpack-5.1.0', + ), + 'jetpack_installed_plugin' => array( + 'replacement' => 'jetpack_plugin_installed', + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_holiday_snow_option_name' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_holiday_chance_of_snow' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_holiday_snow_js_url' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_is_holiday_snow_season' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_holiday_snow_option_updated' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_holiday_snowing' => array( + 'replacement' => null, + 'version' => 'jetpack-6.0.0', + ), + 'jetpack_sso_auth_cookie_expirtation' => array( + 'replacement' => 'jetpack_sso_auth_cookie_expiration', + 'version' => 'jetpack-6.1.0', + ), + 'jetpack_cache_plans' => array( + 'replacement' => null, + 'version' => 'jetpack-6.1.0', + ), - /* - * Format: - * deprecated_filter_name => replacement_name - * - * If there is no replacement, use null for replacement_name - */ - $deprecated_list = array( - 'jetpack_bail_on_shortcode' => 'jetpack_shortcodes_to_include', - 'wpl_sharing_2014_1' => null, - 'jetpack-tools-to-include' => 'jetpack_tools_to_include', - 'jetpack_identity_crisis_options_to_check' => null, - 'update_option_jetpack_single_user_site' => null, - 'audio_player_default_colors' => null, - 'add_option_jetpack_featured_images_enabled' => null, - 'add_option_jetpack_update_details' => null, - 'add_option_jetpack_updates' => null, - 'add_option_jetpack_network_name' => null, - 'add_option_jetpack_network_allow_new_registrations' => null, - 'add_option_jetpack_network_add_new_users' => null, - 'add_option_jetpack_network_site_upload_space' => null, - 'add_option_jetpack_network_upload_file_types' => null, - 'add_option_jetpack_network_enable_administration_menus' => null, - 'add_option_jetpack_is_multi_site' => null, - 'add_option_jetpack_is_main_network' => null, - 'add_option_jetpack_main_network_site' => null, - 'jetpack_sync_all_registered_options' => null, - 'jetpack_has_identity_crisis' => 'jetpack_sync_error_idc_validation', - 'jetpack_is_post_mailable' => null, - 'jetpack_seo_site_host' => null, - 'jetpack_installed_plugin' => 'jetpack_plugin_installed', - 'jetpack_holiday_snow_option_name' => null, - 'jetpack_holiday_chance_of_snow' => null, - 'jetpack_holiday_snow_js_url' => null, - 'jetpack_is_holiday_snow_season' => null, - 'jetpack_holiday_snow_option_updated' => null, - 'jetpack_holiday_snowing' => null, - 'jetpack_sso_auth_cookie_expirtation' => 'jetpack_sso_auth_cookie_expiration', - 'jetpack_cache_plans' => null, - 'jetpack_updated_theme' => 'jetpack_updated_themes', - 'jetpack_lazy_images_skip_image_with_atttributes' => 'jetpack_lazy_images_skip_image_with_attributes', - 'jetpack_enable_site_verification' => null, - 'can_display_jetpack_manage_notice' => null, - // Removed in Jetpack 7.3.0 - 'atd_load_scripts' => null, - 'atd_http_post_timeout' => null, - 'atd_http_post_error' => null, - 'atd_service_domain' => null, - 'jetpack_widget_authors_exclude' => 'jetpack_widget_authors_params', - // Removed in Jetpack 7.9.0 - 'jetpack_pwa_manifest' => null, - 'jetpack_pwa_background_color' => null, + 'jetpack_lazy_images_skip_image_with_atttributes' => array( + 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes', + 'version' => 'jetpack-6.5.0', + ), + 'jetpack_enable_site_verification' => array( + 'replacement' => null, + 'version' => 'jetpack-6.5.0', + ), + 'can_display_jetpack_manage_notice' => array( + 'replacement' => null, + 'version' => 'jetpack-7.3.0', + ), + 'atd_http_post_timeout' => array( + 'replacement' => null, + 'version' => 'jetpack-7.3.0', + ), + 'atd_service_domain' => array( + 'replacement' => null, + 'version' => 'jetpack-7.3.0', + ), + 'atd_load_scripts' => array( + 'replacement' => null, + 'version' => 'jetpack-7.3.0', + ), + 'jetpack_widget_authors_exclude' => array( + 'replacement' => 'jetpack_widget_authors_params', + 'version' => 'jetpack-7.7.0', + ), + // Removed in Jetpack 7.9.0. + 'jetpack_pwa_manifest' => array( + 'replacement' => null, + 'version' => 'jetpack-7.9.0', + ), + 'jetpack_pwa_background_color' => array( + 'replacement' => null, + 'version' => 'jetpack-7.9.0', + ), + 'jetpack_check_mobile' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_stylesheet' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_template' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_theme_menu' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'minileven_show_featured_images' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'minileven_attachment_size' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'instagram_cache_oembed_api_response_body' => array( + 'replacement' => null, + 'version' => 'jetpack-9.1.0', + ), + 'jetpack_can_make_outbound_https' => array( + 'replacement' => null, + 'version' => 'jetpack-9.1.0', + ), ); - // This is a silly loop depth. Better way? - foreach ( $deprecated_list as $hook => $hook_alt ) { - if ( has_action( $hook ) ) { - foreach ( $wp_filter[ $hook ] as $func => $values ) { - foreach ( $values as $hooked ) { - if ( is_callable( $hooked['function'] ) ) { - $function_name = 'an anonymous function'; - } else { - $function_name = $hooked['function']; - } - _deprecated_function( $hook . ' used for ' . $function_name, null, $hook_alt ); - } - } + foreach ( $filter_deprecated_list as $tag => $args ) { + if ( has_filter( $tag ) ) { + apply_filters_deprecated( $tag, array( null ), $args['version'], $args['replacement'] ); + } + } + + $action_deprecated_list = array( + 'jetpack_updated_theme' => array( + 'replacement' => 'jetpack_updated_themes', + 'version' => 'jetpack-6.2.0', + ), + 'atd_http_post_error' => array( + 'replacement' => null, + 'version' => 'jetpack-7.3.0', + ), + 'mobile_reject_mobile' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'mobile_force_mobile' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'mobile_app_promo_download' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'mobile_setup' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_footer_before' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'wp_mobile_theme_footer' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'minileven_credits' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_header_before' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + 'jetpack_mobile_header_after' => array( + 'replacement' => null, + 'version' => 'jetpack-8.3.0', + ), + ); + + foreach ( $action_deprecated_list as $tag => $args ) { + if ( has_action( $tag ) ) { + do_action_deprecated( $tag, array(), $args['version'], $args['replacement'] ); } } } @@ -6413,8 +6560,8 @@ endif; * - Absolute URLs `http://domain.com/feh.png` * - Domain root relative URLs `/feh.png` * - * @param $css string: The raw CSS -- should be read in directly from the file. - * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from. + * @param string $css The raw CSS -- should be read in directly from the file. + * @param string $css_file_url The URL that the file can be accessed at, for calculating paths from. * * @return mixed|string */ @@ -6487,8 +6634,10 @@ endif; * * add_filter( 'jetpack_implode_frontend_css', '__return_false' ); * + * @param bool $travis_test Is this a test run. + * * @since 3.2 - **/ + */ public function implode_frontend_css( $travis_test = false ) { $do_implode = true; if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { @@ -6509,19 +6658,19 @@ endif; */ $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode ); - // Do not use the imploded file when default behavior was altered through the filter + // Do not use the imploded file when default behavior was altered through the filter. if ( ! $do_implode ) { return; } - // We do not want to use the imploded file in dev mode, or if not connected - if ( ( new Status() )->is_development_mode() || ! self::is_active() ) { + // We do not want to use the imploded file in dev mode, or if not connected. + if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) { if ( ! $travis_test ) { return; } } - // Do not use the imploded file if sharing css was dequeued via the sharing settings screen + // Do not use the imploded file if sharing css was dequeued via the sharing settings screen. if ( get_option( 'sharedaddy_disable_resources' ) ) { return; } @@ -6547,6 +6696,14 @@ endif; wp_style_add_data( 'jetpack_css', 'rtl', 'replace' ); } + /** + * Removes styles that are part of concatenated group. + * + * @param string $tag Style tag. + * @param string $handle Style handle. + * + * @return string + */ function concat_remove_style_loader_tag( $tag, $handle ) { if ( in_array( $handle, $this->concatenated_style_handles ) ) { $tag = ''; @@ -6559,24 +6716,6 @@ endif; } /** - * Add an async attribute to scripts that can be loaded asynchronously. - * https://www.w3schools.com/tags/att_script_async.asp - * - * @since 7.7.0 - * - * @param string $tag The <script> tag for the enqueued script. - * @param string $handle The script's registered handle. - * @param string $src The script's source URL. - */ - public function script_add_async( $tag, $handle, $src ) { - if ( in_array( $handle, $this->async_script_handles, true ) ) { - return preg_replace( '/^<script /i', '<script async ', $tag ); - } - - return $tag; - } - - /* * Check the heartbeat data * * Organizes the heartbeat data by severity. For example, if the site @@ -6598,7 +6737,7 @@ endif; foreach ( $raw_data as $stat => $value ) { - // Check jetpack version + // Check jetpack version. if ( 'version' == $stat ) { if ( version_compare( $value, JETPACK__VERSION, '<' ) ) { $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__VERSION; @@ -6606,7 +6745,7 @@ endif; } } - // Check WP version + // Check WP version. if ( 'wp-version' == $stat ) { if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) { $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_WP_VERSION; @@ -6614,15 +6753,15 @@ endif; } } - // Check PHP version + // Check PHP version. if ( 'php-version' == $stat ) { if ( version_compare( PHP_VERSION, JETPACK__MINIMUM_PHP_VERSION, '<' ) ) { - $caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_PHP_VERSION; + $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_PHP_VERSION; continue; } } - // Check ID crisis + // Check ID crisis. if ( 'identitycrisis' == $stat ) { if ( 'yes' == $value ) { $bad[ $stat ] = $value; @@ -6630,7 +6769,7 @@ endif; } } - // The rest are good :) + // The rest are good :). $good[ $stat ] = $value; } @@ -6643,8 +6782,7 @@ endif; return $filtered_data; } - - /* + /** * This method is used to organize all options that can be reset * without disconnecting Jetpack. * @@ -6658,81 +6796,25 @@ endif; return Jetpack_Options::get_options_for_reset(); } - /* + /** * Strip http:// or https:// from a url, replaces forward slash with ::, * so we can bring them directly to their site in calypso. * - * @param string | url - * @return string | url without the guff - */ - public static function build_raw_urls( $url ) { - $strip_http = '/.*?:\/\//i'; - $url = preg_replace( $strip_http, '', $url ); - $url = str_replace( '/', '::', $url ); - return $url; - } - - /** - * Stores and prints out domains to prefetch for page speed optimization. + * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix * - * @param mixed $new_urls + * @param string $url URL. + * @return string url without the guff. */ - public static function dns_prefetch( $new_urls = null ) { - static $prefetch_urls = array(); - if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) { - echo "\r\n"; - foreach ( $prefetch_urls as $this_prefetch_url ) { - printf( "<link rel='dns-prefetch' href='%s'/>\r\n", esc_attr( $this_prefetch_url ) ); - } - } elseif ( ! empty( $new_urls ) ) { - if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) { - add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ); - } - foreach ( (array) $new_urls as $this_new_url ) { - $prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) ); - } - $prefetch_urls = array_unique( $prefetch_urls ); - } - } - - public function wp_dashboard_setup() { - if ( self::is_active() ) { - add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 ); - } - - if ( has_action( 'jetpack_dashboard_widget' ) ) { - $jetpack_logo = new Jetpack_Logo(); - $widget_title = sprintf( - wp_kses( - /* translators: Placeholder is a Jetpack logo. */ - __( 'Stats <span>by %s</span>', 'jetpack' ), - array( 'span' => array() ) - ), - $jetpack_logo->get_jp_emblem( true ) - ); - - wp_add_dashboard_widget( - 'jetpack_summary_widget', - $widget_title, - array( __CLASS__, 'dashboard_widget' ) - ); - wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION ); - wp_style_add_data( 'jetpack-dashboard-widget', 'rtl', 'replace' ); - - // If we're inactive and not in development mode, sort our box to the top. - if ( ! self::is_active() && ! ( new Status() )->is_development_mode() ) { - global $wp_meta_boxes; - - $dashboard = $wp_meta_boxes['dashboard']['normal']['core']; - $ours = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] ); + public static function build_raw_urls( $url ) { + _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' ); - $wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard ); - } - } + return ( new Status() )->get_site_suffix( $url ); } /** - * @param mixed $result Value for the user's option + * Get the user's meta box order. + * + * @param array $sorted Value for the user's option. * @return mixed */ function get_user_option_meta_box_order_dashboard( $sorted ) { @@ -6750,7 +6832,7 @@ endif; $key = array_search( 'dashboard_stats', $ids_array ); if ( false !== $key ) { - // If we've found that exact value in the option (and not `google_dashboard_stats` for example) + // If we've found that exact value in the option (and not `google_dashboard_stats` for example). $ids_array[ $key ] = 'jetpack_summary_widget'; $sorted[ $box_context ] = implode( ',', $ids_array ); // We've found it, stop searching, and just return. @@ -6761,91 +6843,29 @@ endif; return $sorted; } - public static function dashboard_widget() { - /** - * Fires when the dashboard is loaded. - * - * @since 3.4.0 - */ - do_action( 'jetpack_dashboard_widget' ); - } - - public static function dashboard_widget_footer() { - ?> - <footer> - - <div class="protect"> - <?php if ( self::is_module_active( 'protect' ) ) : ?> - <h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3> - <p><?php echo esc_html_x( 'Blocked malicious login attempts', '{#} Blocked malicious login attempts -- number is on a prior line, text is a caption.', 'jetpack' ); ?></p> - <?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! ( new Status() )->is_development_mode() ) : ?> - <a href=" - <?php - echo esc_url( - wp_nonce_url( - self::admin_url( - array( - 'action' => 'activate', - 'module' => 'protect', - ) - ), - 'jetpack_activate-protect' - ) - ); - ?> - " class="button button-jetpack" title="<?php esc_attr_e( 'Protect helps to keep you secure from brute-force login attacks.', 'jetpack' ); ?>"> - <?php esc_html_e( 'Activate Protect', 'jetpack' ); ?> - </a> - <?php else : ?> - <?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?> - <?php endif; ?> - </div> - - <div class="akismet"> - <?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?> - <h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3> - <p><?php echo esc_html_x( 'Spam comments blocked by Akismet.', '{#} Spam comments blocked by Akismet -- number is on a prior line, text is a caption.', 'jetpack' ); ?></p> - <?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?> - <a href=" - <?php - echo esc_url( - wp_nonce_url( - add_query_arg( - array( - 'action' => 'activate', - 'plugin' => 'akismet/akismet.php', - ), - admin_url( 'plugins.php' ) - ), - 'activate-plugin_akismet/akismet.php' - ) - ); - ?> - " class="button button-jetpack"> - <?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?> - </a> - <?php else : ?> - <p><a href="<?php echo esc_url( 'https://akismet.com/?utm_source=jetpack&utm_medium=link&utm_campaign=Jetpack%20Dashboard%20Widget%20Footer%20Link' ); ?>"><?php esc_html_e( 'Akismet can help to keep your blog safe from spam!', 'jetpack' ); ?></a></p> - <?php endif; ?> - </div> - - </footer> - <?php - } - - /* + /** * Adds a "blank" column in the user admin table to display indication of user connection. + * + * @param array $columns User list table columns. + * + * @return array */ function jetpack_icon_user_connected( $columns ) { $columns['user_jetpack'] = ''; return $columns; } - /* + /** * Show Jetpack icon if the user is linked. + * + * @param string $val HTML for the icon. + * @param string $col User list table column. + * @param int $user_id User ID. + * + * @return string */ function jetpack_show_user_connected_icon( $val, $col, $user_id ) { - if ( 'user_jetpack' == $col && self::is_user_connected( $user_id ) ) { + if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) { $jetpack_logo = new Jetpack_Logo(); $emblem_html = sprintf( '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>', @@ -6858,7 +6878,7 @@ endif; return $val; } - /* + /** * Style the Jetpack user column */ function jetpack_user_col_style() { @@ -6874,7 +6894,7 @@ endif; height: 20px; } .jp-emblem-user-admin path { - fill: #00BE28; + fill: #069e08; } </style> <?php @@ -6929,15 +6949,6 @@ endif; } /** - * @deprecated - * - * @see Automattic\Jetpack\Sync\Modules\Users::is_function_in_backtrace - */ - public static function is_function_in_backtrace() { - _deprecated_function( __METHOD__, 'jetpack-7.6.0' ); - } - - /** * Given a minified path, and a non-minified path, will return * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy. * @@ -6946,8 +6957,8 @@ endif; * * @since 5.6.0 * - * @param string $min_path - * @param string $non_min_path + * @param string $min_path Minimized path. + * @param string $non_min_path Non-minimized path. * @return string The URL to the file */ public static function get_file_url_for_environment( $min_path, $non_min_path ) { @@ -6955,13 +6966,14 @@ endif; } /** - * Checks for whether Jetpack Backup & Scan is enabled. - * Will return true if the state of Backup & Scan is anything except "unavailable". + * Checks for whether Jetpack Backup is enabled. + * Will return true if the state of Backup is anything except "unavailable". * * @return bool|int|mixed */ public static function is_rewind_enabled() { - if ( ! self::is_active() ) { + // Rewind is a paid feature, therefore requires a user-level connection. + if ( ! static::connection()->has_connected_owner() ) { return false; } @@ -7005,15 +7017,25 @@ endif; } /** - * Checks whether or not TOS has been agreed upon. - * Will return true if a user has clicked to register, or is already connected. + * Returns the hostname with protocol for Calypso. + * Used for developing Jetpack with Calypso. + * + * @since 8.4.0 + * + * @return string Calypso host. */ - public static function jetpack_tos_agreed() { - _deprecated_function( 'Jetpack::jetpack_tos_agreed', 'Jetpack 7.9.0', '\Automattic\Jetpack\Terms_Of_Service->has_agreed' ); - - $terms_of_service = new Terms_Of_Service(); - return $terms_of_service->has_agreed(); - + public static function get_calypso_host() { + $calypso_env = self::get_calypso_env(); + switch ( $calypso_env ) { + case 'development': + return 'http://calypso.localhost:3000/'; + case 'wpcalypso': + return 'https://wpcalypso.wordpress.com/'; + case 'horizon': + return 'https://horizon.wordpress.com/'; + default: + return 'https://wordpress.com/'; + } } /** @@ -7033,21 +7055,20 @@ endif; ? array( 'sso' ) : array(); - if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) { + if ( Jetpack_Options::get_option( 'active_modules_initialized' ) ) { + $active_modules = Jetpack_Options::get_option( 'active_modules' ); self::delete_active_modules(); self::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, $send_state_messages ); } else { - self::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages ); + // Default modules that don't require a user were already activated on site_register. + // This time let's activate only those that require a user, this assures we don't reactivate manually deactivated modules while the site was connected only at a site level. + self::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages, null, true ); + Jetpack_Options::update_option( 'active_modules_initialized', true ); } - // Since this is a fresh connection, be sure to clear out IDC options - Jetpack_IDC::clear_all_idc_options(); - Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' ); - - // Start nonce cleaner - wp_clear_scheduled_hook( 'jetpack_clean_nonces' ); - wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' ); + // Since this is a fresh connection, be sure to clear out IDC options. + Identity_Crisis::clear_all_idc_options(); if ( $send_state_messages ) { self::state( 'message', 'authorized' ); @@ -7070,31 +7091,6 @@ endif; return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true ); } - /* - * Deprecated manage functions - */ - function prepare_manage_jetpack_notice() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function manage_activate_screen() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function admin_jetpack_manage_notice() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function opt_out_jetpack_manage_url() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function opt_in_jetpack_manage_url() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function opt_in_jetpack_manage_notice() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - function can_display_jetpack_manage_notice() { - _deprecated_function( __METHOD__, 'jetpack-7.3' ); - } - /** * Clean leftoveruser meta. * @@ -7106,7 +7102,7 @@ endif; */ public static function user_meta_cleanup( $user_id ) { $meta_keys = array( - // AtD removed from Jetpack 7.3 + // AtD removed from Jetpack 7.3. 'AtD_options', 'AtD_check_when', 'AtD_guess_lang', @@ -7121,16 +7117,174 @@ endif; } /** - * Checks if a Jetpack site is both active and not in development. + * Checks if a Jetpack site is both active and not in offline mode. + * + * This is a DRY function to avoid repeating `Jetpack::is_connection_ready && ! Automattic\Jetpack\Status->is_offline_mode`. + * + * @since 8.8.0 + * + * @return bool True if Jetpack is active and not in offline mode. + */ + public static function is_active_and_not_offline_mode() { + if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() ) { + return false; + } + return true; + } + + /** + * Returns the list of products that we have available for purchase. + * + * This method will not take current purchases or upgrades into account + * but is instead a static list of products Jetpack offers with some + * corresponding sales text/materials. + * + * @param bool $show_legacy Determine if we should include legacy product/plan details. + * @return array + */ + public static function get_products_for_purchase( $show_legacy = false ) { + $products = array(); + + $products['backup'] = array( + 'title' => __( 'Jetpack Backup', 'jetpack' ), + 'slug' => 'jetpack_backup_t1_yearly', + 'description' => __( 'Never lose a word, image, page, or time worrying about your site with automated backups & one-click restores.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array( 'security' ), + 'features' => array( + _x( 'Real-time cloud backups', 'Backup Product Feature', 'jetpack' ), + _x( '10GB of backup storage', 'Backup Product Feature', 'jetpack' ), + _x( '30-day archive & activity log', 'Backup Product Feature', 'jetpack' ), + _x( 'One-click restores', 'Backup Product Feature', 'jetpack' ), + ), + ); + + $products['scan'] = array( + 'title' => __( 'Jetpack Scan', 'jetpack' ), + 'slug' => 'jetpack_scan', + 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array( 'security' ), + 'features' => array( + _x( 'Automated daily scanning', 'Scan Product Feature', 'jetpack' ), + _x( 'One-click fixes for most issues', 'Scan Product Feature', 'jetpack' ), + _x( 'Instant email notifications', 'Scan Product Feature', 'jetpack' ), + ), + ); + + $products['search'] = array( + 'title' => __( 'Jetpack Site Search', 'jetpack' ), + 'slug' => 'jetpack_search', + 'description' => __( 'Help your site visitors find answers instantly so they keep reading and buying. Great for sites with a lot of content.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array(), + 'features' => array( + _x( 'Instant search and indexing', 'Search Product Feature', 'jetpack' ), + _x( 'Powerful filtering', 'Search Product Feature', 'jetpack' ), + _x( 'Supports 29 languages', 'Search Product Feature', 'jetpack' ), + _x( 'Spelling correction', 'Search Product Feature', 'jetpack' ), + ), + ); + + $products['akismet'] = array( + 'title' => __( 'Jetpack Anti-Spam', 'jetpack' ), + 'slug' => 'jetpack_anti_spam', + 'description' => __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array( 'security' ), + 'features' => array( + _x( 'Comment and form spam protection', 'Anti-Spam Product Feature', 'jetpack' ), + _x( 'Powered by Akismet', 'Anti-Spam Product Feature', 'jetpack' ), + _x( 'Block spam without CAPTCHAs', 'Anti-Spam Product Feature', 'jetpack' ), + _x( 'Advanced stats', 'Anti-Spam Product Feature', 'jetpack' ), + ), + ); + + $products['security'] = array( + 'title' => __( 'Security', 'jetpack' ), + 'slug' => 'jetpack_security_t1_yearly', + 'description' => __( 'Comprehensive site security, including Backup, Scan, and Anti-spam.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array(), + 'features' => array( + _x( 'Real-time cloud backups with 10GB storage', 'Security Tier 1 Feature', 'jetpack' ), + _x( 'Automated real-time malware scan', 'Security Daily Plan Feature', 'jetpack' ), + _x( 'One-click fixes for most threats', 'Security Daily Plan Feature', 'jetpack' ), + _x( 'Comment & form spam protection', 'Security Daily Plan Feature', 'jetpack' ), + ), + ); + + $products['videopress'] = array( + 'title' => __( 'Jetpack VideoPress', 'jetpack' ), + 'slug' => 'jetpack_videopress', + 'description' => __( 'High-quality, ad-free video built specifically for WordPress.', 'jetpack' ), + 'show_promotion' => true, + 'discount_percent' => 50, + 'included_in_plans' => array(), + 'features' => array( + _x( '1TB of storage', 'VideoPress Product Feature', 'jetpack' ), + _x( 'Built into WordPress editor', 'VideoPress Product Feature', 'jetpack' ), + _x( 'Ad-free and brandable player', 'VideoPress Product Feature', 'jetpack' ), + _x( 'Unlimited users', 'VideoPress Product Feature', 'jetpack' ), + ), + ); + + if ( $show_legacy ) { + $products['jetpack_backup_daily'] = array( + 'title' => __( 'Jetpack Backup', 'jetpack' ), + 'slug' => 'jetpack_backup_daily', + 'description' => __( 'Never lose a word, image, page, or time worrying about your site with automated backups & one-click restores.', 'jetpack' ), + 'show_promotion' => false, + 'discount_percent' => 0, + 'included_in_plans' => array(), + 'features' => array( + _x( 'Automated daily backups (off-site)', 'Backup Product Feature', 'jetpack' ), + _x( 'One-click restores', 'Backup Product Feature', 'jetpack' ), + _x( 'Unlimited backup storage', 'Backup Product Feature', 'jetpack' ), + ), + ); + } + + return $products; + } + + /** + * Register product descriptions for partner coupon usage. + * + * @since $$next_version$$ * - * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_development_mode`. + * @param array $products An array of registered products. + * + * @return array + */ + public function get_partner_coupon_product_descriptions( $products ) { + return array_merge( $products, self::get_products_for_purchase( true ) ); + } + + /** + * Determine if the current user is allowed to make Jetpack purchases without + * a WordPress.com account * - * @return bool True if Jetpack is active and not in development. + * @return boolean True if the user can make purchases, false if not */ - public static function is_active_and_not_development_mode() { - if ( ! self::is_active() || ( new Status() )->is_development_mode() ) { + public static function current_user_can_purchase() { + + // The site must be site-connected to Jetpack (no users connected). + if ( ! self::connection()->is_site_connection() ) { return false; } + + // Make sure only administrators can make purchases. + if ( ! current_user_can( 'manage_options' ) ) { + return false; + } + return true; } + } |