PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.2
Jetpack – WP Security, Backup, Speed, & Growth v10.2
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack.php

class.jetpack.php in Jetpack – WP Security, Backup, Speed, & Growth 10.2, at class.jetpack.php

7,516 lines 244.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Assets;
4 use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
5 use Automattic\Jetpack\Config;
6 use Automattic\Jetpack\Connection\Client;
7 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
8 use Automattic\Jetpack\Connection\Nonce_Handler;
9 use Automattic\Jetpack\Connection\Plugin_Storage as Connection_Plugin_Storage;
10 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
11 use Automattic\Jetpack\Connection\Secrets;
12 use Automattic\Jetpack\Connection\Tokens;
13 use Automattic\Jetpack\Connection\Utils as Connection_Utils;
14 use Automattic\Jetpack\Connection\Webhooks as Connection_Webhooks;
15 use Automattic\Jetpack\Constants;
16 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
17 use Automattic\Jetpack\Identity_Crisis;
18 use Automattic\Jetpack\Licensing;
19 use Automattic\Jetpack\Partner;
20 use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
21 use Automattic\Jetpack\Redirect;
22 use Automattic\Jetpack\Status;
23 use Automattic\Jetpack\Sync\Functions;
24 use Automattic\Jetpack\Sync\Health;
25 use Automattic\Jetpack\Sync\Sender;
26 use Automattic\Jetpack\Sync\Users;
27 use Automattic\Jetpack\Terms_Of_Service;
28 use Automattic\Jetpack\Tracking;
29
30 /*
31 Options:
32 jetpack_options (array)
33 An array of options.
34 @see Jetpack_Options::get_option_names()
35
36 jetpack_register (string)
37 Temporary verification secrets.
38
39 jetpack_activated (int)
40 1: the plugin was activated normally
41 2: the plugin was activated on this site because of a network-wide activation
42 3: the plugin was auto-installed
43 4: the plugin was manually disconnected (but is still installed)
44
45 jetpack_active_modules (array)
46 Array of active module slugs.
47
48 jetpack_do_activate (bool)
49 Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
50 */
51
52 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php';
53
54 class Jetpack {
55 public $xmlrpc_server = null;
56
57 /**
58 * @var array The handles of styles that are concatenated into jetpack.css.
59 *
60 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
61 */
62 public $concatenated_style_handles = array(
63 'jetpack-carousel-swiper-css',
64 'jetpack-carousel',
65 'grunion.css',
66 'the-neverending-homepage',
67 'jetpack_likes',
68 'jetpack_related-posts',
69 'sharedaddy',
70 'jetpack-slideshow',
71 'presentations',
72 'quiz',
73 'jetpack-subscriptions',
74 'jetpack-responsive-videos-style',
75 'jetpack-social-menu',
76 'tiled-gallery',
77 'jetpack_display_posts_widget',
78 'gravatar-profile-widget',
79 'goodreads-widget',
80 'jetpack_social_media_icons_widget',
81 'jetpack-top-posts-widget',
82 'jetpack_image_widget',
83 'jetpack-my-community-widget',
84 'jetpack-authors-widget',
85 'wordads',
86 'eu-cookie-law-style',
87 'flickr-widget-style',
88 'jetpack-search-widget',
89 'jetpack-simple-payments-widget-style',
90 'jetpack-widget-social-icons-styles',
91 'wpcom_instagram_widget',
92 );
93
94 /**
95 * Contains all assets that have had their URL rewritten to minified versions.
96 *
97 * @var array
98 */
99 static $min_assets = array();
100
101 public $plugins_to_deactivate = array(
102 'stats' => array( 'stats/stats.php', 'WordPress.com Stats' ),
103 'shortlinks' => array( 'stats/stats.php', 'WordPress.com Stats' ),
104 'sharedaddy' => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
105 'twitter-widget' => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
106 'contact-form' => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
107 'contact-form' => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
108 'custom-css' => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
109 'random-redirect' => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
110 'videopress' => array( 'video/video.php', 'VideoPress' ),
111 'widget-visibility' => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
112 'widget-visibility' => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
113 'sharedaddy' => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
114 'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
115 'latex' => array( 'wp-latex/wp-latex.php', 'WP LaTeX' ),
116 );
117
118 /**
119 * Map of roles we care about, and their corresponding minimum capabilities.
120 *
121 * @deprecated 7.6 Use Automattic\Jetpack\Roles::$capability_translations instead.
122 *
123 * @access public
124 * @static
125 *
126 * @var array
127 */
128 public static $capability_translations = array(
129 'administrator' => 'manage_options',
130 'editor' => 'edit_others_posts',
131 'author' => 'publish_posts',
132 'contributor' => 'edit_posts',
133 'subscriber' => 'read',
134 );
135
136 /**
137 * Map of modules that have conflicts with plugins and should not be auto-activated
138 * if the plugins are active. Used by filter_default_modules
139 *
140 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
141 * change `module-slug` and add this to your plugin:
142 *
143 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
144 * function my_jetpack_get_default_modules( $modules ) {
145 * return array_diff( $modules, array( 'module-slug' ) );
146 * }
147 *
148 * @var array
149 */
150 private $conflicting_plugins = array(
151 'comments' => array(
152 'Intense Debate' => 'intensedebate/intensedebate.php',
153 'Disqus' => 'disqus-comment-system/disqus.php',
154 'Livefyre' => 'livefyre-comments/livefyre.php',
155 'Comments Evolved for WordPress' => 'gplus-comments/comments-evolved.php',
156 'Google+ Comments' => 'google-plus-comments/google-plus-comments.php',
157 'WP-SpamShield Anti-Spam' => 'wp-spamshield/wp-spamshield.php',
158 ),
159 'comment-likes' => array(
160 'Epoch' => 'epoch/plugincore.php',
161 ),
162 'contact-form' => array(
163 'Contact Form 7' => 'contact-form-7/wp-contact-form-7.php',
164 'Gravity Forms' => 'gravityforms/gravityforms.php',
165 'Contact Form Plugin' => 'contact-form-plugin/contact_form.php',
166 'Easy Contact Forms' => 'easy-contact-forms/easy-contact-forms.php',
167 'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php',
168 'Ninja Forms' => 'ninja-forms/ninja-forms.php',
169 ),
170 'latex' => array(
171 'LaTeX for WordPress' => 'latex/latex.php',
172 'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php',
173 'Easy WP LaTeX' => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
174 'MathJax-LaTeX' => 'mathjax-latex/mathjax-latex.php',
175 'Enable Latex' => 'enable-latex/enable-latex.php',
176 'WP QuickLaTeX' => 'wp-quicklatex/wp-quicklatex.php',
177 ),
178 'protect' => array(
179 'Limit Login Attempts' => 'limit-login-attempts/limit-login-attempts.php',
180 'Captcha' => 'captcha/captcha.php',
181 'Brute Force Login Protection' => 'brute-force-login-protection/brute-force-login-protection.php',
182 'Login Security Solution' => 'login-security-solution/login-security-solution.php',
183 'WPSecureOps Brute Force Protect' => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
184 'BulletProof Security' => 'bulletproof-security/bulletproof-security.php',
185 'SiteGuard WP Plugin' => 'siteguard/siteguard.php',
186 'Security-protection' => 'security-protection/security-protection.php',
187 'Login Security' => 'login-security/login-security.php',
188 'Botnet Attack Blocker' => 'botnet-attack-blocker/botnet-attack-blocker.php',
189 'Wordfence Security' => 'wordfence/wordfence.php',
190 'All In One WP Security & Firewall' => 'all-in-one-wp-security-and-firewall/wp-security.php',
191 'iThemes Security' => 'better-wp-security/better-wp-security.php',
192 ),
193 'random-redirect' => array(
194 'Random Redirect 2' => 'random-redirect-2/random-redirect.php',
195 ),
196 'related-posts' => array(
197 'YARPP' => 'yet-another-related-posts-plugin/yarpp.php',
198 'WordPress Related Posts' => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
199 'nrelate Related Content' => 'nrelate-related-content/nrelate-related.php',
200 'Contextual Related Posts' => 'contextual-related-posts/contextual-related-posts.php',
201 'Related Posts for WordPress' => 'microkids-related-posts/microkids-related-posts.php',
202 'outbrain' => 'outbrain/outbrain.php',
203 'Shareaholic' => 'shareaholic/shareaholic.php',
204 'Sexybookmarks' => 'sexybookmarks/shareaholic.php',
205 ),
206 'sharedaddy' => array(
207 'AddThis' => 'addthis/addthis_social_widget.php',
208 'Add To Any' => 'add-to-any/add-to-any.php',
209 'ShareThis' => 'share-this/sharethis.php',
210 'Shareaholic' => 'shareaholic/shareaholic.php',
211 ),
212 'seo-tools' => array(
213 'WordPress SEO by Yoast' => 'wordpress-seo/wp-seo.php',
214 'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
215 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
216 'All in One SEO Pack Pro' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
217 'The SEO Framework' => 'autodescription/autodescription.php',
218 'Rank Math' => 'seo-by-rank-math/rank-math.php',
219 'Slim SEO' => 'slim-seo/slim-seo.php',
220 ),
221 'verification-tools' => array(
222 'WordPress SEO by Yoast' => 'wordpress-seo/wp-seo.php',
223 'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
224 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
225 'All in One SEO Pack Pro' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
226 'The SEO Framework' => 'autodescription/autodescription.php',
227 'Rank Math' => 'seo-by-rank-math/rank-math.php',
228 'Slim SEO' => 'slim-seo/slim-seo.php',
229 ),
230 'widget-visibility' => array(
231 'Widget Logic' => 'widget-logic/widget_logic.php',
232 'Dynamic Widgets' => 'dynamic-widgets/dynamic-widgets.php',
233 ),
234 'sitemaps' => array(
235 'Google XML Sitemaps' => 'google-sitemap-generator/sitemap.php',
236 'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
237 'Google XML Sitemaps for qTranslate' => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
238 'XML Sitemap & Google News feeds' => 'xml-sitemap-feed/xml-sitemap.php',
239 'Google Sitemap by BestWebSoft' => 'google-sitemap-plugin/google-sitemap-plugin.php',
240 'WordPress SEO by Yoast' => 'wordpress-seo/wp-seo.php',
241 'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
242 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
243 'All in One SEO Pack Pro' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
244 'The SEO Framework' => 'autodescription/autodescription.php',
245 'Sitemap' => 'sitemap/sitemap.php',
246 'Simple Wp Sitemap' => 'simple-wp-sitemap/simple-wp-sitemap.php',
247 'Simple Sitemap' => 'simple-sitemap/simple-sitemap.php',
248 'XML Sitemaps' => 'xml-sitemaps/xml-sitemaps.php',
249 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
250 'Rank Math' => 'seo-by-rank-math/rank-math.php',
251 'Slim SEO' => 'slim-seo/slim-seo.php',
252 ),
253 'lazy-images' => array(
254 'Lazy Load' => 'lazy-load/lazy-load.php',
255 'BJ Lazy Load' => 'bj-lazy-load/bj-lazy-load.php',
256 'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
257 ),
258 );
259
260 /**
261 * Plugins for which we turn off our Facebook OG Tags implementation.
262 *
263 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
264 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
265 *
266 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
267 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
268 */
269 private $open_graph_conflicting_plugins = array(
270 '2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
271 // 2 Click Social Media Buttons
272 'add-link-to-facebook/add-link-to-facebook.php', // Add Link to Facebook
273 'add-meta-tags/add-meta-tags.php', // Add Meta Tags
274 'complete-open-graph/complete-open-graph.php', // Complete Open Graph
275 'easy-facebook-share-thumbnails/esft.php', // Easy Facebook Share Thumbnail
276 'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
277 // Open Graph Meta Tags by Heateor
278 'facebook/facebook.php', // Facebook (official plugin)
279 'facebook-awd/AWD_facebook.php', // Facebook AWD All in one
280 'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
281 // Facebook Featured Image & OG Meta Tags
282 'facebook-meta-tags/facebook-metatags.php', // Facebook Meta Tags
283 'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
284 // Facebook Open Graph Meta Tags for WordPress
285 'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag
286 'facebook-thumb-fixer/_facebook-thumb-fixer.php', // Facebook Thumb Fixer
287 'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
288 // Fedmich's Facebook Open Graph Meta
289 'network-publisher/networkpub.php', // Network Publisher
290 'nextgen-facebook/nextgen-facebook.php', // NextGEN Facebook OG
291 'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
292 // NextScripts SNAP
293 'og-tags/og-tags.php', // OG Tags
294 'opengraph/opengraph.php', // Open Graph
295 'open-graph-protocol-framework/open-graph-protocol-framework.php',
296 // Open Graph Protocol Framework
297 'seo-facebook-comments/seofacebook.php', // SEO Facebook Comments
298 'seo-ultimate/seo-ultimate.php', // SEO Ultimate
299 'sexybookmarks/sexy-bookmarks.php', // Shareaholic
300 'shareaholic/sexy-bookmarks.php', // Shareaholic
301 'sharepress/sharepress.php', // SharePress
302 'simple-facebook-connect/sfc.php', // Simple Facebook Connect
303 'social-discussions/social-discussions.php', // Social Discussions
304 'social-sharing-toolkit/social_sharing_toolkit.php', // Social Sharing Toolkit
305 'socialize/socialize.php', // Socialize
306 'squirrly-seo/squirrly.php', // SEO by SQUIRRLY™
307 'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
308 // Tweet, Like, Google +1 and Share
309 'wordbooker/wordbooker.php', // Wordbooker
310 'wpsso/wpsso.php', // WordPress Social Sharing Optimization
311 'wp-caregiver/wp-caregiver.php', // WP Caregiver
312 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
313 // WP Facebook Like Send & Open Graph Meta
314 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol
315 'wp-ogp/wp-ogp.php', // WP-OGP
316 'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin
317 'wp-fb-share-like-button/wp_fb_share-like_widget.php', // WP Facebook Like Button
318 'open-graph-metabox/open-graph-metabox.php', // Open Graph Metabox
319 'seo-by-rank-math/rank-math.php', // Rank Math.
320 'slim-seo/slim-seo.php', // Slim SEO
321 );
322
323 /**
324 * Plugins for which we turn off our Twitter Cards Tags implementation.
325 */
326 private $twitter_cards_conflicting_plugins = array(
327 // 'twitter/twitter.php', // The official one handles this on its own.
328 // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
329 'eewee-twitter-card/index.php', // Eewee Twitter Card
330 'ig-twitter-cards/ig-twitter-cards.php', // IG:Twitter Cards
331 'jm-twitter-cards/jm-twitter-cards.php', // JM Twitter Cards
332 'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
333 // Pure Web Brilliant's Social Graph Twitter Cards Extension
334 'twitter-cards/twitter-cards.php', // Twitter Cards
335 'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
336 'wp-to-twitter/wp-to-twitter.php', // WP to Twitter
337 'wp-twitter-cards/twitter_cards.php', // WP Twitter Cards
338 'seo-by-rank-math/rank-math.php', // Rank Math.
339 'slim-seo/slim-seo.php', // Slim SEO
340 );
341
342 /**
343 * Message to display in admin_notice
344 *
345 * @var string
346 */
347 public $message = '';
348
349 /**
350 * Error to display in admin_notice
351 *
352 * @var string
353 */
354 public $error = '';
355
356 /**
357 * Modules that need more privacy description.
358 *
359 * @var string
360 */
361 public $privacy_checks = '';
362
363 /**
364 * Stats to record once the page loads
365 *
366 * @var array
367 */
368 public $stats = array();
369
370 /**
371 * Jetpack_Sync object
372 */
373 public $sync;
374
375 /**
376 * Verified data for JSON authorization request
377 */
378 public $json_api_authorization_request = array();
379
380 /**
381 * @var Automattic\Jetpack\Connection\Manager
382 */
383 protected $connection_manager;
384
385 /**
386 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
387 */
388 public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
389
390 /**
391 * Holds an instance of Automattic\Jetpack\A8c_Mc_Stats
392 *
393 * @var Automattic\Jetpack\A8c_Mc_Stats
394 */
395 public $a8c_mc_stats_instance;
396
397 /**
398 * Constant for login redirect key.
399 *
400 * @var string
401 * @since 8.4.0
402 */
403 public static $jetpack_redirect_login = 'jetpack_connect_login_redirect';
404
405 /**
406 * Holds the singleton instance of this class
407 *
408 * @since 2.3.3
409 * @var Jetpack
410 */
411 static $instance = false;
412
413 /**
414 * Singleton
415 *
416 * @static
417 */
418 public static function init() {
419 if ( ! self::$instance ) {
420 self::$instance = new Jetpack();
421 add_action( 'plugins_loaded', array( self::$instance, 'plugin_upgrade' ) );
422 add_action( 'jetpack_idc_disconnect', array( __CLASS__, 'on_idc_disconnect' ) );
423 }
424
425 return self::$instance;
426 }
427
428 /**
429 * Must never be called statically
430 */
431 function plugin_upgrade() {
432 if ( self::is_connection_ready() ) {
433 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
434 if ( JETPACK__VERSION != $version ) {
435 // Prevent multiple upgrades at once - only a single process should trigger
436 // an upgrade to avoid stampedes
437 if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
438 return;
439 }
440
441 // Set a short lock to prevent multiple instances of the upgrade
442 set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
443
444 // check which active modules actually exist and remove others from active_modules list
445 $unfiltered_modules = self::get_active_modules();
446 $modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
447 if ( array_diff( $unfiltered_modules, $modules ) ) {
448 self::update_active_modules( $modules );
449 }
450
451 add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
452
453 // Upgrade to 4.3.0
454 if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
455 Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
456 }
457
458 // Make sure Markdown for posts gets turned back on
459 if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
460 update_option( 'wpcom_publish_posts_with_markdown', true );
461 }
462
463 /*
464 * Minileven deprecation. 8.3.0.
465 * Only delete options if not using
466 * the replacement standalone Minileven plugin.
467 */
468 if (
469 ! self::is_plugin_active( 'minileven-master/minileven.php' )
470 && ! self::is_plugin_active( 'minileven/minileven.php' )
471 ) {
472 if ( get_option( 'wp_mobile_custom_css' ) ) {
473 delete_option( 'wp_mobile_custom_css' );
474 }
475 if ( get_option( 'wp_mobile_excerpt' ) ) {
476 delete_option( 'wp_mobile_excerpt' );
477 }
478 if ( get_option( 'wp_mobile_featured_images' ) ) {
479 delete_option( 'wp_mobile_featured_images' );
480 }
481 if ( get_option( 'wp_mobile_app_promos' ) ) {
482 delete_option( 'wp_mobile_app_promos' );
483 }
484 }
485
486 // Upgrade to 8.4.0.
487 if ( Jetpack_Options::get_option( 'ab_connect_banner_green_bar' ) ) {
488 Jetpack_Options::delete_option( 'ab_connect_banner_green_bar' );
489 }
490
491 // Update to 8.8.x (WordPress 5.5 Compatibility).
492 if ( Jetpack_Options::get_option( 'autoupdate_plugins' ) ) {
493 $updated = update_site_option(
494 'auto_update_plugins',
495 array_unique(
496 array_merge(
497 (array) Jetpack_Options::get_option( 'autoupdate_plugins', array() ),
498 (array) get_site_option( 'auto_update_plugins', array() )
499 )
500 )
501 );
502
503 if ( $updated ) {
504 Jetpack_Options::delete_option( 'autoupdate_plugins' );
505 } // Should we have some type of fallback if something fails here?
506 }
507
508 if ( did_action( 'wp_loaded' ) ) {
509 self::upgrade_on_load();
510 } else {
511 add_action(
512 'wp_loaded',
513 array( __CLASS__, 'upgrade_on_load' )
514 );
515 }
516 }
517 }
518 }
519
520 /**
521 * Runs upgrade routines that need to have modules loaded.
522 */
523 static function upgrade_on_load() {
524
525 // Not attempting any upgrades if jetpack_modules_loaded did not fire.
526 // This can happen in case Jetpack has been just upgraded and is
527 // being initialized late during the page load. In this case we wait
528 // until the next proper admin page load with Jetpack active.
529 if ( ! did_action( 'jetpack_modules_loaded' ) ) {
530 delete_transient( self::$plugin_upgrade_lock_key );
531
532 return;
533 }
534
535 self::maybe_set_version_option();
536
537 if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
538 Jetpack_Widget_Conditions::migrate_post_type_rules();
539 }
540
541 if (
542 class_exists( 'Jetpack_Sitemap_Manager' )
543 && version_compare( JETPACK__VERSION, '5.3', '>=' )
544 ) {
545 do_action( 'jetpack_sitemaps_purge_data' );
546 }
547
548 // Delete old stats cache
549 delete_option( 'jetpack_restapi_stats_cache' );
550
551 delete_transient( self::$plugin_upgrade_lock_key );
552 }
553
554 /**
555 * Saves all the currently active modules to options.
556 * Also fires Action hooks for each newly activated and deactivated module.
557 *
558 * @param $modules Array Array of active modules to be saved in options.
559 *
560 * @return $success bool true for success, false for failure.
561 */
562 static function update_active_modules( $modules ) {
563 $current_modules = Jetpack_Options::get_option( 'active_modules', array() );
564 $active_modules = self::get_active_modules();
565 $new_active_modules = array_diff( $modules, $current_modules );
566 $new_inactive_modules = array_diff( $active_modules, $modules );
567 $new_current_modules = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
568 $reindexed_modules = array_values( $new_current_modules );
569 $success = Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
570
571 foreach ( $new_active_modules as $module ) {
572 /**
573 * Fires when a specific module is activated.
574 *
575 * @since 1.9.0
576 *
577 * @param string $module Module slug.
578 * @param boolean $success whether the module was activated. @since 4.2
579 */
580 do_action( 'jetpack_activate_module', $module, $success );
581 /**
582 * Fires when a module is activated.
583 * The dynamic part of the filter, $module, is the module slug.
584 *
585 * @since 1.9.0
586 *
587 * @param string $module Module slug.
588 */
589 do_action( "jetpack_activate_module_$module", $module );
590 }
591
592 foreach ( $new_inactive_modules as $module ) {
593 /**
594 * Fired after a module has been deactivated.
595 *
596 * @since 4.2.0
597 *
598 * @param string $module Module slug.
599 * @param boolean $success whether the module was deactivated.
600 */
601 do_action( 'jetpack_deactivate_module', $module, $success );
602 /**
603 * Fires when a module is deactivated.
604 * The dynamic part of the filter, $module, is the module slug.
605 *
606 * @since 1.9.0
607 *
608 * @param string $module Module slug.
609 */
610 do_action( "jetpack_deactivate_module_$module", $module );
611 }
612
613 return $success;
614 }
615
616 static function delete_active_modules() {
617 self::update_active_modules( array() );
618 }
619
620 /**
621 * Adds a hook to plugins_loaded at a priority that's currently the earliest
622 * available.
623 */
624 public function add_configure_hook() {
625 global $wp_filter;
626
627 $current_priority = has_filter( 'plugins_loaded', array( $this, 'configure' ) );
628 if ( false !== $current_priority ) {
629 remove_action( 'plugins_loaded', array( $this, 'configure' ), $current_priority );
630 }
631
632 $taken_priorities = array_map( 'intval', array_keys( $wp_filter['plugins_loaded']->callbacks ) );
633 sort( $taken_priorities );
634
635 $first_priority = array_shift( $taken_priorities );
636
637 if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
638 $new_priority = - PHP_INT_MAX;
639 } else {
640 $new_priority = $first_priority - 1;
641 }
642
643 add_action( 'plugins_loaded', array( $this, 'configure' ), $new_priority );
644 }
645
646 /**
647 * Constructor. Initializes WordPress hooks
648 */
649 private function __construct() {
650 /*
651 * Check for and alert any deprecated hooks
652 */
653 add_action( 'init', array( $this, 'deprecated_hooks' ) );
654
655 // Note how this runs at an earlier plugin_loaded hook intentionally to accomodate for other plugins.
656 add_action( 'plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
657 add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
658 add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
659 add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
660
661 add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
662
663 add_filter(
664 'jetpack_signature_check_token',
665 array( __CLASS__, 'verify_onboarding_token' ),
666 10,
667 3
668 );
669
670 /**
671 * Prepare Gutenberg Editor functionality
672 */
673 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
674 add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
675 add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
676 add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_extended_blocks' ), 9 );
677 add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
678
679 add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
680
681 // Unlink user before deleting the user from WP.com.
682 add_action( 'deleted_user', array( $this, 'disconnect_user' ), 10, 1 );
683 add_action( 'remove_user_from_blog', array( $this, 'disconnect_user' ), 10, 1 );
684
685 add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
686
687 add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
688 add_action( 'login_init', array( $this, 'login_init' ) );
689
690 // Set up the REST authentication hooks.
691 Connection_Rest_Authentication::init();
692
693 add_action( 'admin_init', array( $this, 'admin_init' ) );
694 add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
695
696 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
697
698 // Filter the dashboard meta box order to swap the new one in in place of the old one.
699 add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
700
701 // WordPress dashboard widget.
702 require_once JETPACK__PLUGIN_DIR . 'class-jetpack-stats-dashboard-widget.php';
703 add_action( 'wp_dashboard_setup', array( new Jetpack_Stats_Dashboard_Widget(), 'init' ) );
704
705 // Returns HTTPS support status.
706 add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
707
708 add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
709
710 add_action( 'wp_ajax_jetpack_recommendations_banner', array( 'Jetpack_Recommendations_Banner', 'ajax_callback' ) );
711
712 add_action( 'wp_loaded', array( $this, 'register_assets' ) );
713
714 /**
715 * These actions run checks to load additional files.
716 * They check for external files or plugins, so they need to run as late as possible.
717 */
718 add_action( 'wp_head', array( $this, 'check_open_graph' ), 1 );
719 add_action( 'web_stories_story_head', array( $this, 'check_open_graph' ), 1 );
720 add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ), 999 );
721 add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
722
723 add_filter( 'plugins_url', array( 'Jetpack', 'maybe_min_asset' ), 1, 3 );
724 add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2 );
725 add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
726
727 add_filter( 'profile_update', array( 'Jetpack', 'user_meta_cleanup' ) );
728
729 add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
730 add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
731
732 require_once JETPACK__PLUGIN_DIR . 'class-jetpack-pre-connection-jitms.php';
733 $jetpack_jitm_messages = ( new Jetpack_Pre_Connection_JITMs() );
734 add_filter( 'jetpack_pre_connection_jitms', array( $jetpack_jitm_messages, 'add_pre_connection_jitms' ) );
735
736 /*
737 * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
738 * We should make sure to only do this for front end links.
739 */
740 if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
741 add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
742 add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
743
744 /*
745 * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
746 * so they point moderation links on emails to Calypso.
747 */
748 jetpack_require_lib( 'functions.wp-notify' );
749 add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
750 add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
751 }
752
753 add_action(
754 'plugins_loaded',
755 function () {
756 if ( User_Agent_Info::is_mobile_app() ) {
757 add_filter( 'get_edit_post_link', '__return_empty_string' );
758 }
759 }
760 );
761
762 // Update the site's Jetpack plan and products from API on heartbeats.
763 add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
764
765 /**
766 * This is the hack to concatenate all css files into one.
767 * For description and reasoning see the implode_frontend_css method.
768 *
769 * Super late priority so we catch all the registered styles.
770 */
771 if ( ! is_admin() ) {
772 add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
773 add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
774 }
775
776 // Actually push the stats on shutdown.
777 if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
778 add_action( 'shutdown', array( $this, 'push_stats' ) );
779 }
780
781 // After a successful connection.
782 add_action( 'jetpack_site_registered', array( $this, 'activate_default_modules_on_site_register' ) );
783 add_action( 'jetpack_site_registered', array( $this, 'handle_unique_registrations_stats' ) );
784
785 // Actions for Manager::authorize().
786 add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
787 add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
788 add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
789
790 add_action( 'jetpack_client_authorize_error', array( Jetpack_Client_Server::class, 'client_authorize_error' ) );
791 add_filter( 'jetpack_client_authorize_already_authorized_url', array( Jetpack_Client_Server::class, 'client_authorize_already_authorized_url' ) );
792 add_action( 'jetpack_client_authorize_processing', array( Jetpack_Client_Server::class, 'client_authorize_processing' ) );
793 add_filter( 'jetpack_client_authorize_fallback_url', array( Jetpack_Client_Server::class, 'client_authorize_fallback_url' ) );
794
795 // Filters for the Manager::get_token() urls and request body.
796 add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
797 add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
798
799 // Filter for the `jetpack/v4/connection/data` API response.
800 add_filter( 'jetpack_current_user_connection_data', array( __CLASS__, 'filter_jetpack_current_user_connection_data' ) );
801
802 // Actions for successful reconnect.
803 add_action( 'jetpack_reconnection_completed', array( $this, 'reconnection_completed' ) );
804
805 // Actions for successful disconnect.
806 add_action( 'jetpack_site_disconnected', array( $this, 'jetpack_site_disconnected' ) );
807
808 // Actions for licensing.
809 Licensing::instance()->initialize();
810
811 // Filters for Sync Callables.
812 add_filter( 'jetpack_sync_callable_whitelist', array( $this, 'filter_sync_callable_whitelist' ), 10, 1 );
813 add_filter( 'jetpack_sync_multisite_callable_whitelist', array( $this, 'filter_sync_multisite_callable_whitelist' ), 10, 1 );
814
815 // Make resources use static domain when possible.
816 add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) );
817
818 // Validate the domain names in Jetpack development versions.
819 add_action( 'jetpack_pre_register', array( get_called_class(), 'registration_check_domains' ) );
820 }
821
822 /**
823 * Before everything else starts getting initalized, we need to initialize Jetpack using the
824 * Config object.
825 */
826 public function configure() {
827 $config = new Config();
828
829 foreach (
830 array(
831 'sync',
832 'jitm',
833 )
834 as $feature
835 ) {
836 $config->ensure( $feature );
837 }
838
839 $config->ensure(
840 'connection',
841 array(
842 'slug' => 'jetpack',
843 'name' => 'Jetpack',
844 )
845 );
846
847 if ( ! $this->connection_manager ) {
848 $this->connection_manager = new Connection_Manager( 'jetpack' );
849
850 /**
851 * Filter to activate Jetpack Connection UI.
852 * INTERNAL USE ONLY.
853 *
854 * @since 9.5.0
855 *
856 * @param bool false Whether to activate the Connection UI.
857 */
858 if ( apply_filters( 'jetpack_connection_ui_active', false ) ) {
859 Automattic\Jetpack\ConnectionUI\Admin::init();
860 }
861 }
862
863 /*
864 * Load things that should only be in Network Admin.
865 *
866 * For now blow away everything else until a more full
867 * understanding of what is needed at the network level is
868 * available
869 */
870 if ( is_multisite() ) {
871 $network = Jetpack_Network::init();
872 $network->set_connection( $this->connection_manager );
873 }
874
875 if ( self::is_connection_ready() ) {
876 add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
877
878 Jetpack_Heartbeat::init();
879 if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
880 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
881 Jetpack_Search_Performance_Logger::init();
882 }
883 }
884
885 // Initialize remote file upload request handlers.
886 $this->add_remote_request_handlers();
887
888 /*
889 * Enable enhanced handling of previewing sites in Calypso
890 */
891 if ( self::is_connection_ready() ) {
892 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
893 add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
894 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
895 add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
896 }
897
898 if ( ( new Tracking( $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
899 add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
900 } else {
901 /**
902 * Initialize tracking right after the user agrees to the terms of service.
903 */
904 add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
905 }
906 }
907
908 /**
909 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
910 * initialization code.
911 *
912 * @action plugins_loaded
913 */
914 public function late_initialization() {
915 add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
916
917 Partner::init();
918
919 /**
920 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
921 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
922 *
923 * @since 8.1.0
924 *
925 * @param Jetpack $jetpack the main plugin class object.
926 */
927 do_action( 'jetpack_loaded', $this );
928
929 add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
930 }
931
932 /**
933 * Sets up the XMLRPC request handlers.
934 *
935 * @deprecated since 7.7.0
936 * @see Automattic\Jetpack\Connection\Manager::setup_xmlrpc_handlers()
937 *
938 * @param array $request_params Incoming request parameters.
939 * @param Boolean $is_active Whether the connection is currently active.
940 * @param Boolean $is_signed Whether the signature check has been successful.
941 * @param Jetpack_XMLRPC_Server $xmlrpc_server (optional) An instance of the server to use instead of instantiating a new one.
942 */
943 public function setup_xmlrpc_handlers(
944 $request_params,
945 $is_active,
946 $is_signed,
947 Jetpack_XMLRPC_Server $xmlrpc_server = null
948 ) {
949 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::setup_xmlrpc_handlers' );
950
951 if ( ! $this->connection_manager ) {
952 $this->connection_manager = new Connection_Manager();
953 }
954
955 return $this->connection_manager->setup_xmlrpc_handlers(
956 $request_params,
957 $is_active,
958 $is_signed,
959 $xmlrpc_server
960 );
961 }
962
963 /**
964 * Initialize REST API registration connector.
965 *
966 * @deprecated since 7.7.0
967 * @see Automattic\Jetpack\Connection\Manager::initialize_rest_api_registration_connector()
968 */
969 public function initialize_rest_api_registration_connector() {
970 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::initialize_rest_api_registration_connector' );
971
972 if ( ! $this->connection_manager ) {
973 $this->connection_manager = new Connection_Manager();
974 }
975
976 $this->connection_manager->initialize_rest_api_registration_connector();
977 }
978
979 /**
980 * This is ported over from the manage module, which has been deprecated and baked in here.
981 *
982 * @param $domains
983 */
984 function add_wpcom_to_allowed_redirect_hosts( $domains ) {
985 add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) );
986 }
987
988 /**
989 * Return $domains, with 'wordpress.com' appended.
990 * This is ported over from the manage module, which has been deprecated and baked in here.
991 *
992 * @param $domains
993 * @return array
994 */
995 function allow_wpcom_domain( $domains ) {
996 if ( empty( $domains ) ) {
997 $domains = array();
998 }
999 $domains[] = 'wordpress.com';
1000 return array_unique( $domains );
1001 }
1002
1003 function point_edit_post_links_to_calypso( $default_url, $post_id ) {
1004 $post = get_post( $post_id );
1005
1006 if ( empty( $post ) ) {
1007 return $default_url;
1008 }
1009
1010 $post_type = $post->post_type;
1011
1012 // Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
1013 // https://en.support.wordpress.com/custom-post-types/
1014 $allowed_post_types = array(
1015 'post',
1016 'page',
1017 'jetpack-portfolio',
1018 'jetpack-testimonial',
1019 );
1020
1021 if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
1022 return $default_url;
1023 }
1024
1025 return Redirect::get_url(
1026 'calypso-edit-' . $post_type,
1027 array(
1028 'path' => $post_id,
1029 )
1030 );
1031 }
1032
1033 function point_edit_comment_links_to_calypso( $url ) {
1034 // Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1035 wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
1036
1037 return Redirect::get_url(
1038 'calypso-edit-comment',
1039 array(
1040 'path' => $query_args['amp;c'],
1041 )
1042 );
1043
1044 }
1045
1046 /**
1047 * Extend Sync callables with Jetpack Plugin functions.
1048 *
1049 * @param array $callables list of callables.
1050 *
1051 * @return array list of callables.
1052 */
1053 public function filter_sync_callable_whitelist( $callables ) {
1054
1055 // Jetpack Functions.
1056 $jetpack_callables = array(
1057 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
1058 'updates' => array( 'Jetpack', 'get_updates' ),
1059 'active_modules' => array( 'Jetpack', 'get_active_modules' ),
1060 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
1061 );
1062 $callables = array_merge( $callables, $jetpack_callables );
1063
1064 // Jetpack_SSO_Helpers.
1065 if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
1066 $sso_helpers = array(
1067 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
1068 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
1069 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
1070 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
1071 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
1072 );
1073 $callables = array_merge( $callables, $sso_helpers );
1074 }
1075
1076 return $callables;
1077 }
1078
1079 /**
1080 * Extend Sync multisite callables with Jetpack Plugin functions.
1081 *
1082 * @param array $callables list of callables.
1083 *
1084 * @return array list of callables.
1085 */
1086 public function filter_sync_multisite_callable_whitelist( $callables ) {
1087
1088 // Jetpack Funtions.
1089 $jetpack_multisite_callables = array(
1090 'network_name' => array( 'Jetpack', 'network_name' ),
1091 'network_allow_new_registrations' => array( 'Jetpack', 'network_allow_new_registrations' ),
1092 'network_add_new_users' => array( 'Jetpack', 'network_add_new_users' ),
1093 'network_site_upload_space' => array( 'Jetpack', 'network_site_upload_space' ),
1094 'network_upload_file_types' => array( 'Jetpack', 'network_upload_file_types' ),
1095 'network_enable_administration_menus' => array( 'Jetpack', 'network_enable_administration_menus' ),
1096 );
1097 $callables = array_merge( $callables, $jetpack_multisite_callables );
1098
1099 return $callables;
1100 }
1101
1102 /**
1103 * Deprecated
1104 * Please use Automattic\Jetpack\JITMS\JITM::jetpack_track_last_sync_callback instead.
1105 *
1106 * @param array $params The action parameters.
1107 *
1108 * @deprecated since 9.8.
1109 */
1110 function jetpack_track_last_sync_callback( $params ) {
1111 _deprecated_function( __METHOD__, 'jetpack-9.8', '\Automattic\Jetpack\JITMS\JITM->jetpack_track_last_sync_callback' );
1112 return Automattic\Jetpack\JITMS\JITM::get_instance()->jetpack_track_last_sync_callback( $params );
1113 }
1114
1115 function jetpack_connection_banner_callback() {
1116 check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1117
1118 // Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1119 if (
1120 isset( $_REQUEST['dismissBanner'] ) &&
1121 ! Jetpack_Connection_Banner::force_display()
1122 ) {
1123 Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1124 wp_send_json_success();
1125 }
1126
1127 wp_die();
1128 }
1129
1130 /**
1131 * Removes all XML-RPC methods that are not `jetpack.*`.
1132 * Only used in our alternate XML-RPC endpoint, where we want to
1133 * ensure that Core and other plugins' methods are not exposed.
1134 *
1135 * @deprecated since 7.7.0
1136 * @see Automattic\Jetpack\Connection\Manager::remove_non_jetpack_xmlrpc_methods()
1137 *
1138 * @param array $methods A list of registered WordPress XMLRPC methods.
1139 * @return array Filtered $methods
1140 */
1141 public function remove_non_jetpack_xmlrpc_methods( $methods ) {
1142 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::remove_non_jetpack_xmlrpc_methods' );
1143
1144 if ( ! $this->connection_manager ) {
1145 $this->connection_manager = new Connection_Manager();
1146 }
1147
1148 return $this->connection_manager->remove_non_jetpack_xmlrpc_methods( $methods );
1149 }
1150
1151 /**
1152 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
1153 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
1154 * security/firewall policies, we provide our own alternate XML RPC API endpoint
1155 * which is accessible via a different URI. Most of the below is copied directly
1156 * from /xmlrpc.php so that we're replicating it as closely as possible.
1157 *
1158 * @deprecated since 7.7.0
1159 * @see Automattic\Jetpack\Connection\Manager::alternate_xmlrpc()
1160 */
1161 public function alternate_xmlrpc() {
1162 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::alternate_xmlrpc' );
1163
1164 if ( ! $this->connection_manager ) {
1165 $this->connection_manager = new Connection_Manager();
1166 }
1167
1168 $this->connection_manager->alternate_xmlrpc();
1169 }
1170
1171 /**
1172 * The callback for the JITM ajax requests.
1173 *
1174 * @deprecated since 7.9.0
1175 */
1176 function jetpack_jitm_ajax_callback() {
1177 _deprecated_function( __METHOD__, 'jetpack-7.9' );
1178 }
1179
1180 /**
1181 * If there are any stats that need to be pushed, but haven't been, push them now.
1182 */
1183 function push_stats() {
1184 if ( ! empty( $this->stats ) ) {
1185 $this->do_stats( 'server_side' );
1186 }
1187 }
1188
1189 /**
1190 * Sets the Jetpack custom capabilities.
1191 *
1192 * @param string[] $caps Array of the user's capabilities.
1193 * @param string $cap Capability name.
1194 * @param int $user_id The user ID.
1195 * @param array $args Adds the context to the cap. Typically the object ID.
1196 */
1197 public function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
1198 switch ( $cap ) {
1199 case 'jetpack_manage_modules':
1200 case 'jetpack_activate_modules':
1201 case 'jetpack_deactivate_modules':
1202 $caps = array( 'manage_options' );
1203 break;
1204 case 'jetpack_configure_modules':
1205 $caps = array( 'manage_options' );
1206 break;
1207 case 'jetpack_manage_autoupdates':
1208 $caps = array(
1209 'manage_options',
1210 'update_plugins',
1211 );
1212 break;
1213 case 'jetpack_network_admin_page':
1214 case 'jetpack_network_settings_page':
1215 $caps = array( 'manage_network_plugins' );
1216 break;
1217 case 'jetpack_network_sites_page':
1218 $caps = array( 'manage_sites' );
1219 break;
1220 case 'jetpack_admin_page':
1221 $is_offline_mode = ( new Status() )->is_offline_mode();
1222 if ( $is_offline_mode ) {
1223 $caps = array( 'manage_options' );
1224 break;
1225 } else {
1226 $caps = array( 'read' );
1227 }
1228 break;
1229 }
1230 return $caps;
1231 }
1232
1233 /**
1234 * Require a Jetpack authentication.
1235 *
1236 * @deprecated since 7.7.0
1237 * @see Automattic\Jetpack\Connection\Manager::require_jetpack_authentication()
1238 */
1239 public function require_jetpack_authentication() {
1240 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::require_jetpack_authentication' );
1241
1242 if ( ! $this->connection_manager ) {
1243 $this->connection_manager = new Connection_Manager();
1244 }
1245
1246 $this->connection_manager->require_jetpack_authentication();
1247 }
1248
1249 /**
1250 * Register assets for use in various modules and the Jetpack admin page.
1251 *
1252 * @uses wp_script_is, wp_register_script, plugins_url
1253 * @action wp_loaded
1254 * @return null
1255 */
1256 public function register_assets() {
1257 if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1258 wp_register_script(
1259 'jetpack-gallery-settings',
1260 Assets::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1261 array( 'media-views' ),
1262 '20121225'
1263 );
1264 }
1265
1266 if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1267 wp_register_script(
1268 'jetpack-twitter-timeline',
1269 Assets::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1270 array( 'jquery' ),
1271 '4.0.0',
1272 true
1273 );
1274 }
1275
1276 if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1277 wp_register_script(
1278 'jetpack-facebook-embed',
1279 Assets::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1280 array(),
1281 null,
1282 true
1283 );
1284
1285 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1286 $fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1287 if ( ! is_numeric( $fb_app_id ) ) {
1288 $fb_app_id = '';
1289 }
1290 wp_localize_script(
1291 'jetpack-facebook-embed',
1292 'jpfbembed',
1293 array(
1294 'appid' => $fb_app_id,
1295 'locale' => $this->get_locale(),
1296 )
1297 );
1298 }
1299
1300 /**
1301 * As jetpack_register_genericons is by default fired off a hook,
1302 * the hook may have already fired by this point.
1303 * So, let's just trigger it manually.
1304 */
1305 require_once JETPACK__PLUGIN_DIR . '_inc/genericons.php';
1306 jetpack_register_genericons();
1307
1308 /**
1309 * Register the social logos
1310 */
1311 require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1312 jetpack_register_social_logos();
1313
1314 if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1315 wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1316 }
1317 }
1318
1319 /**
1320 * Guess locale from language code.
1321 *
1322 * @param string $lang Language code.
1323 * @return string|bool
1324 */
1325 function guess_locale_from_lang( $lang ) {
1326 if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1327 return 'en_US';
1328 }
1329
1330 if ( ! class_exists( 'GP_Locales' ) ) {
1331 if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1332 return false;
1333 }
1334
1335 require JETPACK__GLOTPRESS_LOCALES_PATH;
1336 }
1337
1338 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1339 // WP.com: get_locale() returns 'it'
1340 $locale = GP_Locales::by_slug( $lang );
1341 } else {
1342 // Jetpack: get_locale() returns 'it_IT';
1343 $locale = GP_Locales::by_field( 'facebook_locale', $lang );
1344 }
1345
1346 if ( ! $locale ) {
1347 return false;
1348 }
1349
1350 if ( empty( $locale->facebook_locale ) ) {
1351 if ( empty( $locale->wp_locale ) ) {
1352 return false;
1353 } else {
1354 // Facebook SDK is smart enough to fall back to en_US if a
1355 // locale isn't supported. Since supported Facebook locales
1356 // can fall out of sync, we'll attempt to use the known
1357 // wp_locale value and rely on said fallback.
1358 return $locale->wp_locale;
1359 }
1360 }
1361
1362 return $locale->facebook_locale;
1363 }
1364
1365 /**
1366 * Get the locale.
1367 *
1368 * @return string|bool
1369 */
1370 function get_locale() {
1371 $locale = $this->guess_locale_from_lang( get_locale() );
1372
1373 if ( ! $locale ) {
1374 $locale = 'en_US';
1375 }
1376
1377 return $locale;
1378 }
1379
1380 /**
1381 * Return the network_site_url so that .com knows what network this site is a part of.
1382 *
1383 * @param bool $option
1384 * @return string
1385 */
1386 public function jetpack_main_network_site_option( $option ) {
1387 return network_site_url();
1388 }
1389 /**
1390 * Network Name.
1391 */
1392 static function network_name( $option = null ) {
1393 global $current_site;
1394 return $current_site->site_name;
1395 }
1396 /**
1397 * Does the network allow new user and site registrations.
1398 *
1399 * @return string
1400 */
1401 static function network_allow_new_registrations( $option = null ) {
1402 return ( in_array( get_site_option( 'registration' ), array( 'none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration' ) : 'none' );
1403 }
1404 /**
1405 * Does the network allow admins to add new users.
1406 *
1407 * @return boolian
1408 */
1409 static function network_add_new_users( $option = null ) {
1410 return (bool) get_site_option( 'add_new_users' );
1411 }
1412 /**
1413 * File upload psace left per site in MB.
1414 * -1 means NO LIMIT.
1415 *
1416 * @return number
1417 */
1418 static function network_site_upload_space( $option = null ) {
1419 // value in MB
1420 return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1421 }
1422
1423 /**
1424 * Network allowed file types.
1425 *
1426 * @return string
1427 */
1428 static function network_upload_file_types( $option = null ) {
1429 return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1430 }
1431
1432 /**
1433 * Maximum file upload size set by the network.
1434 *
1435 * @return number
1436 */
1437 static function network_max_upload_file_size( $option = null ) {
1438 // value in KB
1439 return get_site_option( 'fileupload_maxk', 300 );
1440 }
1441
1442 /**
1443 * Lets us know if a site allows admins to manage the network.
1444 *
1445 * @return array
1446 */
1447 static function network_enable_administration_menus( $option = null ) {
1448 return get_site_option( 'menu_items' );
1449 }
1450
1451 /**
1452 * If a user has been promoted to or demoted from admin, we need to clear the
1453 * jetpack_other_linked_admins transient.
1454 *
1455 * @since 4.3.2
1456 * @since 4.4.0 $old_roles is null by default and if it's not passed, the transient is cleared.
1457 *
1458 * @param int $user_id The user ID whose role changed.
1459 * @param string $role The new role.
1460 * @param array $old_roles An array of the user's previous roles.
1461 */
1462 function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1463 if ( 'administrator' == $role
1464 || ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1465 || is_null( $old_roles )
1466 ) {
1467 delete_transient( 'jetpack_other_linked_admins' );
1468 }
1469 }
1470
1471 /**
1472 * Checks to see if there are any other users available to become primary
1473 * Users must both:
1474 * - Be linked to wpcom
1475 * - Be an admin
1476 *
1477 * @return mixed False if no other users are linked, Int if there are.
1478 */
1479 static function get_other_linked_admins() {
1480 $other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1481
1482 if ( false === $other_linked_users ) {
1483 $admins = get_users( array( 'role' => 'administrator' ) );
1484 if ( count( $admins ) > 1 ) {
1485 $available = array();
1486 foreach ( $admins as $admin ) {
1487 if ( self::connection()->is_user_connected( $admin->ID ) ) {
1488 $available[] = $admin->ID;
1489 }
1490 }
1491
1492 $count_connected_admins = count( $available );
1493 if ( count( $available ) > 1 ) {
1494 $other_linked_users = $count_connected_admins;
1495 } else {
1496 $other_linked_users = 0;
1497 }
1498 } else {
1499 $other_linked_users = 0;
1500 }
1501
1502 set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1503 }
1504
1505 return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1506 }
1507
1508 /**
1509 * Return whether we are dealing with a multi network setup or not.
1510 * The reason we are type casting this is because we want to avoid the situation where
1511 * the result is false since when is_main_network_option return false it cases
1512 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1513 * database which could be set to anything as opposed to what this function returns.
1514 *
1515 * @param bool $option
1516 *
1517 * @return boolean
1518 */
1519 public function is_main_network_option( $option ) {
1520 // return '1' or ''
1521 return (string) (bool) self::is_multi_network();
1522 }
1523
1524 /**
1525 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1526 *
1527 * @param string $option
1528 * @return boolean
1529 */
1530 public function is_multisite( $option ) {
1531 return (string) (bool) is_multisite();
1532 }
1533
1534 /**
1535 * Implemented since there is no core is multi network function
1536 * Right now there is no way to tell if we which network is the dominant network on the system
1537 *
1538 * @since 3.3
1539 * @return boolean
1540 */
1541 public static function is_multi_network() {
1542 global $wpdb;
1543
1544 // if we don't have a multi site setup no need to do any more
1545 if ( ! is_multisite() ) {
1546 return false;
1547 }
1548
1549 $num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1550 if ( $num_sites > 1 ) {
1551 return true;
1552 } else {
1553 return false;
1554 }
1555 }
1556
1557 /**
1558 * Trigger an update to the main_network_site when we update the siteurl of a site.
1559 *
1560 * @return null
1561 */
1562 function update_jetpack_main_network_site_option() {
1563 _deprecated_function( __METHOD__, 'jetpack-4.2' );
1564 }
1565 /**
1566 * Triggered after a user updates the network settings via Network Settings Admin Page
1567 */
1568 function update_jetpack_network_settings() {
1569 _deprecated_function( __METHOD__, 'jetpack-4.2' );
1570 // Only sync this info for the main network site.
1571 }
1572
1573 /**
1574 * Get back if the current site is single user site.
1575 *
1576 * @return bool
1577 */
1578 public static function is_single_user_site() {
1579 global $wpdb;
1580
1581 if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1582 $some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1583 set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1584 }
1585 return 1 === (int) $some_users;
1586 }
1587
1588 /**
1589 * Returns true if the site has file write access false otherwise.
1590 *
1591 * @return string ( '1' | '0' )
1592 **/
1593 public static function file_system_write_access() {
1594 if ( ! function_exists( 'get_filesystem_method' ) ) {
1595 require_once ABSPATH . 'wp-admin/includes/file.php';
1596 }
1597
1598 require_once ABSPATH . 'wp-admin/includes/template.php';
1599
1600 $filesystem_method = get_filesystem_method();
1601 if ( $filesystem_method === 'direct' ) {
1602 return 1;
1603 }
1604
1605 ob_start();
1606 $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1607 ob_end_clean();
1608 if ( $filesystem_credentials_are_stored ) {
1609 return 1;
1610 }
1611 return 0;
1612 }
1613
1614 /**
1615 * Finds out if a site is using a version control system.
1616 *
1617 * @return string ( '1' | '0' )
1618 **/
1619 public static function is_version_controlled() {
1620 _deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' );
1621 return (string) (int) Functions::is_version_controlled();
1622 }
1623
1624 /**
1625 * Determines whether the current theme supports featured images or not.
1626 *
1627 * @return string ( '1' | '0' )
1628 */
1629 public static function featured_images_enabled() {
1630 _deprecated_function( __METHOD__, 'jetpack-4.2' );
1631 return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1632 }
1633
1634 /**
1635 * Wrapper for core's get_avatar_url(). This one is deprecated.
1636 *
1637 * @deprecated 4.7 use get_avatar_url instead.
1638 * @param int|string|object $id_or_email A user ID, email address, or comment object
1639 * @param int $size Size of the avatar image
1640 * @param string $default URL to a default image to use if no avatar is available
1641 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
1642 *
1643 * @return array
1644 */
1645 public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1646 _deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1647 return get_avatar_url(
1648 $id_or_email,
1649 array(
1650 'size' => $size,
1651 'default' => $default,
1652 'force_default' => $force_display,
1653 )
1654 );
1655 }
1656 // phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1657 /**
1658 * jetpack_updates is saved in the following schema:
1659 *
1660 * array (
1661 * 'plugins' => (int) Number of plugin updates available.
1662 * 'themes' => (int) Number of theme updates available.
1663 * 'wordpress' => (int) Number of WordPress core updates available.
1664 * 'translations' => (int) Number of translation updates available.
1665 * 'total' => (int) Total of all available updates.
1666 * 'wp_update_version' => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1667 * )
1668 *
1669 * @return array
1670 */
1671 public static function get_updates() {
1672 $update_data = wp_get_update_data();
1673
1674 // Stores the individual update counts as well as the total count.
1675 if ( isset( $update_data['counts'] ) ) {
1676 $updates = $update_data['counts'];
1677 }
1678
1679 // If we need to update WordPress core, let's find the latest version number.
1680 if ( ! empty( $updates['wordpress'] ) ) {
1681 $cur = get_preferred_from_update_core();
1682 if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1683 $updates['wp_update_version'] = $cur->current;
1684 }
1685 }
1686 return isset( $updates ) ? $updates : array();
1687 }
1688 // phpcs:enable
1689
1690 public static function get_update_details() {
1691 $update_details = array(
1692 'update_core' => get_site_transient( 'update_core' ),
1693 'update_plugins' => get_site_transient( 'update_plugins' ),
1694 'update_themes' => get_site_transient( 'update_themes' ),
1695 );
1696 return $update_details;
1697 }
1698
1699 public static function refresh_update_data() {
1700 _deprecated_function( __METHOD__, 'jetpack-4.2' );
1701
1702 }
1703
1704 public static function refresh_theme_data() {
1705 _deprecated_function( __METHOD__, 'jetpack-4.2' );
1706 }
1707
1708 /**
1709 * Is Jetpack active?
1710 * The method only checks if there's an existing token for the master user. It doesn't validate the token.
1711 *
1712 * This method is deprecated since 9.6.0. Please use one of the methods provided by the Manager class in the Connection package,
1713 * or Jetpack::is_connection_ready if you want to know when the Jetpack plugin starts considering the connection ready to be used.
1714 *
1715 * Since this method has a wide spread use, we decided not to throw any deprecation warnings for now.
1716 *
1717 * @deprecated 9.6.0
1718 *
1719 * @return bool
1720 */
1721 public static function is_active() {
1722 return self::connection()->is_active();
1723 }
1724
1725 /**
1726 * Returns true if the current site is connected to WordPress.com and has the minimum requirements to enable Jetpack UI
1727 *
1728 * This method was introduced just before the release of the possibility to use Jetpack without a user connection, while
1729 * it was available only when no_user_testing_mode was enabled. In the near future, this will return is_connected for all
1730 * users and this option will be available by default for everybody.
1731 *
1732 * @since 9.6.0
1733 * @since 9.7.0 returns is_connected in all cases and adds filter to the returned value
1734 *
1735 * @return bool is the site connection ready to be used?
1736 */
1737 public static function is_connection_ready() {
1738 $is_connected = false;
1739
1740 if ( method_exists( self::connection(), 'is_connected' ) ) {
1741 $is_connected = self::connection()->is_connected();
1742 } elseif ( method_exists( self::connection(), 'is_registered' ) ) {
1743 $is_connected = self::connection()->is_registered();
1744 }
1745
1746 /**
1747 * Allows filtering whether the connection is ready to be used. If true, this will enable the Jetpack UI and modules
1748 *
1749 * Modules will be enabled depending on the connection status and if the module requires a connection or user connection.
1750 *
1751 * @since 9.7.0
1752 *
1753 * @param bool $is_connection_ready Is the connection ready?
1754 * @param Automattic\Jetpack\Connection\Manager $connection_manager Instance of the Manager class, can be used to check the connection status.
1755 */
1756 return apply_filters( 'jetpack_is_connection_ready', $is_connected, self::connection() );
1757 }
1758
1759 /**
1760 * Make an API call to WordPress.com for plan status
1761 *
1762 * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom.
1763 *
1764 * @return bool True if plan is updated, false if no update
1765 */
1766 public static function refresh_active_plan_from_wpcom() {
1767 _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' );
1768 return Jetpack_Plan::refresh_from_wpcom();
1769 }
1770
1771 /**
1772 * Get the plan that this Jetpack site is currently using
1773 *
1774 * @deprecated 7.2.0 Use Jetpack_Plan::get.
1775 * @return array Active Jetpack plan details.
1776 */
1777 public static function get_active_plan() {
1778 _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' );
1779 return Jetpack_Plan::get();
1780 }
1781
1782 /**
1783 * Determine whether the active plan supports a particular feature
1784 *
1785 * @deprecated 7.2.0 Use Jetpack_Plan::supports.
1786 * @return bool True if plan supports feature, false if not.
1787 */
1788 public static function active_plan_supports( $feature ) {
1789 _deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' );
1790 return Jetpack_Plan::supports( $feature );
1791 }
1792
1793 /**
1794 * Deprecated: Is Jetpack in development (offline) mode?
1795 *
1796 * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1797 * and themes still use it, and we do not want to flood them with notices.
1798 *
1799 * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1800 *
1801 * @deprecated since 8.0.
1802 */
1803 public static function is_development_mode() {
1804 _deprecated_function( __METHOD__, 'jetpack-8.0', '\Automattic\Jetpack\Status->is_offline_mode' );
1805 return ( new Status() )->is_offline_mode();
1806 }
1807
1808 /**
1809 * Whether the site is currently onboarding or not.
1810 * A site is considered as being onboarded if it currently has an onboarding token.
1811 *
1812 * @since 5.8
1813 *
1814 * @access public
1815 * @static
1816 *
1817 * @return bool True if the site is currently onboarding, false otherwise
1818 */
1819 public static function is_onboarding() {
1820 return Jetpack_Options::get_option( 'onboarding' ) !== false;
1821 }
1822
1823 /**
1824 * Determines reason for Jetpack offline mode.
1825 */
1826 public static function development_mode_trigger_text() {
1827 $status = new Status();
1828
1829 if ( ! $status->is_offline_mode() ) {
1830 return __( 'Jetpack is not in Offline Mode.', 'jetpack' );
1831 }
1832
1833 if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1834 $notice = __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1835 } elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1836 $notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1837 } elseif ( $status->is_local_site() ) {
1838 $notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1839 /** This filter is documented in packages/status/src/class-status.php */
1840 } elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1841 $notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1842 } else {
1843 $notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1844 }
1845
1846 return $notice;
1847
1848 }
1849 /**
1850 * Get Jetpack offline mode notice text and notice class.
1851 *
1852 * Mirrors the checks made in Automattic\Jetpack\Status->is_offline_mode
1853 */
1854 public static function show_development_mode_notice() {
1855 if ( ( new Status() )->is_offline_mode() ) {
1856 $notice = sprintf(
1857 /* translators: %s is a URL */
1858 __( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1859 Redirect::get_url( 'jetpack-support-development-mode' )
1860 );
1861
1862 $notice .= ' ' . self::development_mode_trigger_text();
1863
1864 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1865 }
1866
1867 // Throw up a notice if using a development version and as for feedback.
1868 if ( self::is_development_version() ) {
1869 /* translators: %s is a URL */
1870 $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' ) );
1871
1872 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1873 }
1874 // Throw up a notice if using staging mode
1875 if ( ( new Status() )->is_staging_site() ) {
1876 /* translators: %s is a URL */
1877 $notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1878
1879 echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1880 }
1881 }
1882
1883 /**
1884 * Whether Jetpack's version maps to a public release, or a development version.
1885 */
1886 public static function is_development_version() {
1887 /**
1888 * Allows filtering whether this is a development version of Jetpack.
1889 *
1890 * This filter is especially useful for tests.
1891 *
1892 * @since 4.3.0
1893 *
1894 * @param bool $development_version Is this a develoment version of Jetpack?
1895 */
1896 return (bool) apply_filters(
1897 'jetpack_development_version',
1898 ! preg_match( '/^\d+(\.\d+)+$/', Constants::get_constant( 'JETPACK__VERSION' ) )
1899 );
1900 }
1901
1902 /**
1903 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1904 */
1905 public static function is_user_connected( $user_id = false ) {
1906 _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\is_user_connected' );
1907 return self::connection()->is_user_connected( $user_id );
1908 }
1909
1910 /**
1911 * Get the wpcom user data of the current|specified connected user.
1912 */
1913 public static function get_connected_user_data( $user_id = null ) {
1914 _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Manager\\get_connected_user_data' );
1915 return self::connection()->get_connected_user_data( $user_id );
1916 }
1917
1918 /**
1919 * Get the wpcom email of the current|specified connected user.
1920 */
1921 public static function get_connected_user_email( $user_id = null ) {
1922 if ( ! $user_id ) {
1923 $user_id = get_current_user_id();
1924 }
1925
1926 $xml = new Jetpack_IXR_Client(
1927 array(
1928 'user_id' => $user_id,
1929 )
1930 );
1931 $xml->query( 'wpcom.getUserEmail' );
1932 if ( ! $xml->isError() ) {
1933 return $xml->getResponse();
1934 }
1935 return false;
1936 }
1937
1938 /**
1939 * Get the wpcom email of the master user.
1940 */
1941 public static function get_master_user_email() {
1942 $master_user_id = Jetpack_Options::get_option( 'master_user' );
1943 if ( $master_user_id ) {
1944 return self::get_connected_user_email( $master_user_id );
1945 }
1946 return '';
1947 }
1948
1949 /**
1950 * Whether the current user is the connection owner.
1951 *
1952 * @deprecated since 7.7
1953 *
1954 * @return bool Whether the current user is the connection owner.
1955 */
1956 public function current_user_is_connection_owner() {
1957 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::is_connection_owner' );
1958 return self::connection()->is_connection_owner();
1959 }
1960
1961 /**
1962 * Gets current user IP address.
1963 *
1964 * @param bool $check_all_headers Check all headers? Default is `false`.
1965 *
1966 * @return string Current user IP address.
1967 */
1968 public static function current_user_ip( $check_all_headers = false ) {
1969 if ( $check_all_headers ) {
1970 foreach ( array(
1971 'HTTP_CF_CONNECTING_IP',
1972 'HTTP_CLIENT_IP',
1973 'HTTP_X_FORWARDED_FOR',
1974 'HTTP_X_FORWARDED',
1975 'HTTP_X_CLUSTER_CLIENT_IP',
1976 'HTTP_FORWARDED_FOR',
1977 'HTTP_FORWARDED',
1978 'HTTP_VIA',
1979 ) as $key ) {
1980 if ( ! empty( $_SERVER[ $key ] ) ) {
1981 return $_SERVER[ $key ];
1982 }
1983 }
1984 }
1985
1986 return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1987 }
1988
1989 /**
1990 * Synchronize connected user role changes
1991 */
1992 function user_role_change( $user_id ) {
1993 _deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' );
1994 Users::user_role_change( $user_id );
1995 }
1996
1997 /**
1998 * Loads the currently active modules.
1999 */
2000 public static function load_modules() {
2001 $is_offline_mode = ( new Status() )->is_offline_mode();
2002 if (
2003 ! self::is_connection_ready()
2004 && ! $is_offline_mode
2005 && ! self::is_onboarding()
2006 && (
2007 ! is_multisite()
2008 || ! get_site_option( 'jetpack_protect_active' )
2009 )
2010 ) {
2011 return;
2012 }
2013
2014 $version = Jetpack_Options::get_option( 'version' );
2015 if ( ! $version ) {
2016 $version = $old_version = JETPACK__VERSION . ':' . time();
2017 /** This action is documented in class.jetpack.php */
2018 do_action( 'updating_jetpack_version', $version, false );
2019 Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2020 }
2021 list( $version ) = explode( ':', $version );
2022
2023 $modules = array_filter( self::get_active_modules(), array( 'Jetpack', 'is_module' ) );
2024
2025 $modules_data = array();
2026
2027 // Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
2028 if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
2029 $updated_modules = array();
2030 foreach ( $modules as $module ) {
2031 $modules_data[ $module ] = self::get_module( $module );
2032 if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
2033 continue;
2034 }
2035
2036 if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
2037 continue;
2038 }
2039
2040 $updated_modules[] = $module;
2041 }
2042
2043 $modules = array_diff( $modules, $updated_modules );
2044 }
2045
2046 $is_site_connection = false;
2047
2048 if ( method_exists( self::connection(), 'is_site_connection' ) ) {
2049 $is_site_connection = self::connection()->is_site_connection();
2050 }
2051
2052 foreach ( $modules as $index => $module ) {
2053 // If we're in offline/site-connection mode, disable modules requiring a connection/user connection.
2054 if ( $is_offline_mode || $is_site_connection ) {
2055 // Prime the pump if we need to
2056 if ( empty( $modules_data[ $module ] ) ) {
2057 $modules_data[ $module ] = self::get_module( $module );
2058 }
2059 // If the module requires a connection, but we're in local mode, don't include it.
2060 if ( $is_offline_mode && $modules_data[ $module ]['requires_connection'] ) {
2061 continue;
2062 }
2063
2064 if ( $is_site_connection && $modules_data[ $module ]['requires_user_connection'] ) {
2065 continue;
2066 }
2067 }
2068
2069 if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
2070 continue;
2071 }
2072
2073 if ( ! include_once self::get_module_path( $module ) ) {
2074 unset( $modules[ $index ] );
2075 self::update_active_modules( array_values( $modules ) );
2076 continue;
2077 }
2078
2079 /**
2080 * Fires when a specific module is loaded.
2081 * The dynamic part of the hook, $module, is the module slug.
2082 *
2083 * @since 1.1.0
2084 */
2085 do_action( 'jetpack_module_loaded_' . $module );
2086 }
2087
2088 /**
2089 * Fires when all the modules are loaded.
2090 *
2091 * @since 1.1.0
2092 */
2093 do_action( 'jetpack_modules_loaded' );
2094
2095 // Load module-specific code that is needed even when a module isn't active. Loaded here because code contained therein may need actions such as setup_theme.
2096 require_once JETPACK__PLUGIN_DIR . 'modules/module-extras.php';
2097 }
2098
2099 /**
2100 * Check if Jetpack's REST API compat file should be included
2101 *
2102 * @action plugins_loaded
2103 * @return null
2104 */
2105 public function check_rest_api_compat() {
2106 /**
2107 * Filters the list of REST API compat files to be included.
2108 *
2109 * @since 2.2.5
2110 *
2111 * @param array $args Array of REST API compat files to include.
2112 */
2113 $_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
2114
2115 foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include ) {
2116 require_once $_jetpack_rest_api_compat_include;
2117 }
2118 }
2119
2120 /**
2121 * Gets all plugins currently active in values, regardless of whether they're
2122 * traditionally activated or network activated.
2123 *
2124 * @todo Store the result in core's object cache maybe?
2125 */
2126 public static function get_active_plugins() {
2127 $active_plugins = (array) get_option( 'active_plugins', array() );
2128
2129 if ( is_multisite() ) {
2130 // Due to legacy code, active_sitewide_plugins stores them in the keys,
2131 // whereas active_plugins stores them in the values.
2132 $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
2133 if ( $network_plugins ) {
2134 $active_plugins = array_merge( $active_plugins, $network_plugins );
2135 }
2136 }
2137
2138 sort( $active_plugins );
2139
2140 return array_unique( $active_plugins );
2141 }
2142
2143 /**
2144 * Gets and parses additional plugin data to send with the heartbeat data
2145 *
2146 * @since 3.8.1
2147 *
2148 * @return array Array of plugin data
2149 */
2150 public static function get_parsed_plugin_data() {
2151 if ( ! function_exists( 'get_plugins' ) ) {
2152 require_once ABSPATH . 'wp-admin/includes/plugin.php';
2153 }
2154 /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
2155 $all_plugins = apply_filters( 'all_plugins', get_plugins() );
2156 $active_plugins = self::get_active_plugins();
2157
2158 $plugins = array();
2159 foreach ( $all_plugins as $path => $plugin_data ) {
2160 $plugins[ $path ] = array(
2161 'is_active' => in_array( $path, $active_plugins ),
2162 'file' => $path,
2163 'name' => $plugin_data['Name'],
2164 'version' => $plugin_data['Version'],
2165 'author' => $plugin_data['Author'],
2166 );
2167 }
2168
2169 return $plugins;
2170 }
2171
2172 /**
2173 * Gets and parses theme data to send with the heartbeat data
2174 *
2175 * @since 3.8.1
2176 *
2177 * @return array Array of theme data
2178 */
2179 public static function get_parsed_theme_data() {
2180 $all_themes = wp_get_themes( array( 'allowed' => true ) );
2181 $header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2182
2183 $themes = array();
2184 foreach ( $all_themes as $slug => $theme_data ) {
2185 $theme_headers = array();
2186 foreach ( $header_keys as $header_key ) {
2187 $theme_headers[ $header_key ] = $theme_data->get( $header_key );
2188 }
2189
2190 $themes[ $slug ] = array(
2191 'is_active_theme' => $slug == wp_get_theme()->get_template(),
2192 'slug' => $slug,
2193 'theme_root' => $theme_data->get_theme_root_uri(),
2194 'parent' => $theme_data->parent(),
2195 'headers' => $theme_headers,
2196 );
2197 }
2198
2199 return $themes;
2200 }
2201
2202 /**
2203 * Checks whether a specific plugin is active.
2204 *
2205 * We don't want to store these in a static variable, in case
2206 * there are switch_to_blog() calls involved.
2207 */
2208 public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2209 return in_array( $plugin, self::get_active_plugins() );
2210 }
2211
2212 /**
2213 * Check if Jetpack's Open Graph tags should be used.
2214 * If certain plugins are active, Jetpack's og tags are suppressed.
2215 *
2216 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2217 * @action plugins_loaded
2218 * @return null
2219 */
2220 public function check_open_graph() {
2221 if ( in_array( 'publicize', self::get_active_modules() ) || in_array( 'sharedaddy', self::get_active_modules() ) ) {
2222 add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2223 }
2224
2225 $active_plugins = self::get_active_plugins();
2226
2227 if ( ! empty( $active_plugins ) ) {
2228 foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2229 if ( in_array( $plugin, $active_plugins ) ) {
2230 add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2231 break;
2232 }
2233 }
2234 }
2235
2236 /**
2237 * Allow the addition of Open Graph Meta Tags to all pages.
2238 *
2239 * @since 2.0.3
2240 *
2241 * @param bool false Should Open Graph Meta tags be added. Default to false.
2242 */
2243 if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2244 require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2245 }
2246 }
2247
2248 /**
2249 * Check if Jetpack's Twitter tags should be used.
2250 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2251 *
2252 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2253 * @action plugins_loaded
2254 * @return null
2255 */
2256 public function check_twitter_tags() {
2257
2258 $active_plugins = self::get_active_plugins();
2259
2260 if ( ! empty( $active_plugins ) ) {
2261 foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2262 if ( in_array( $plugin, $active_plugins ) ) {
2263 add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2264 break;
2265 }
2266 }
2267 }
2268
2269 /**
2270 * Allow Twitter Card Meta tags to be disabled.
2271 *
2272 * @since 2.6.0
2273 *
2274 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2275 */
2276 if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2277 require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2278 }
2279 }
2280
2281 /**
2282 * Allows plugins to submit security reports.
2283 *
2284 * @param string $type Report type (login_form, backup, file_scanning, spam)
2285 * @param string $plugin_file Plugin __FILE__, so that we can pull plugin data
2286 * @param array $args See definitions above
2287 */
2288 public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2289 _deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2290 }
2291
2292 /* Jetpack Options API */
2293
2294 public static function get_option_names( $type = 'compact' ) {
2295 return Jetpack_Options::get_option_names( $type );
2296 }
2297
2298 /**
2299 * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
2300 *
2301 * @param string $name Option name
2302 * @param mixed $default (optional)
2303 */
2304 public static function get_option( $name, $default = false ) {
2305 return Jetpack_Options::get_option( $name, $default );
2306 }
2307
2308 /**
2309 * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate.
2310 *
2311 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2312 * @param string $name Option name
2313 * @param mixed $value Option value
2314 */
2315 public static function update_option( $name, $value ) {
2316 _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2317 return Jetpack_Options::update_option( $name, $value );
2318 }
2319
2320 /**
2321 * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate.
2322 *
2323 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2324 * @param array $array array( option name => option value, ... )
2325 */
2326 public static function update_options( $array ) {
2327 _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2328 return Jetpack_Options::update_options( $array );
2329 }
2330
2331 /**
2332 * Deletes the given option. May be passed multiple option names as an array.
2333 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2334 *
2335 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2336 * @param string|array $names
2337 */
2338 public static function delete_option( $names ) {
2339 _deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2340 return Jetpack_Options::delete_option( $names );
2341 }
2342
2343 /**
2344 * Enters a user token into the user_tokens option
2345 *
2346 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Tokens->update_user_token() instead.
2347 *
2348 * @param int $user_id The user id.
2349 * @param string $token The user token.
2350 * @param bool $is_master_user Whether the user is the master user.
2351 * @return bool
2352 */
2353 public static function update_user_token( $user_id, $token, $is_master_user ) {
2354 _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Tokens->update_user_token' );
2355 return ( new Tokens() )->update_user_token( $user_id, $token, $is_master_user );
2356 }
2357
2358 /**
2359 * Returns an array of all PHP files in the specified absolute path.
2360 * Equivalent to glob( "$absolute_path/*.php" ).
2361 *
2362 * @param string $absolute_path The absolute path of the directory to search.
2363 * @return array Array of absolute paths to the PHP files.
2364 */
2365 public static function glob_php( $absolute_path ) {
2366 if ( function_exists( 'glob' ) ) {
2367 return glob( "$absolute_path/*.php" );
2368 }
2369
2370 $absolute_path = untrailingslashit( $absolute_path );
2371 $files = array();
2372 if ( ! $dir = @opendir( $absolute_path ) ) {
2373 return $files;
2374 }
2375
2376 while ( false !== $file = readdir( $dir ) ) {
2377 if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2378 continue;
2379 }
2380
2381 $file = "$absolute_path/$file";
2382
2383 if ( ! is_file( $file ) ) {
2384 continue;
2385 }
2386
2387 $files[] = $file;
2388 }
2389
2390 closedir( $dir );
2391
2392 return $files;
2393 }
2394
2395 public static function activate_new_modules( $redirect = false ) {
2396 if ( ! self::is_connection_ready() && ! ( new Status() )->is_offline_mode() ) {
2397 return;
2398 }
2399
2400 $jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2401 if ( ! $jetpack_old_version ) {
2402 $jetpack_old_version = $version = $old_version = '1.1:' . time();
2403 /** This action is documented in class.jetpack.php */
2404 do_action( 'updating_jetpack_version', $version, false );
2405 Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2406 }
2407
2408 list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2409
2410 if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2411 return;
2412 }
2413
2414 $active_modules = self::get_active_modules();
2415 $reactivate_modules = array();
2416 foreach ( $active_modules as $active_module ) {
2417 $module = self::get_module( $active_module );
2418 if ( ! isset( $module['changed'] ) ) {
2419 continue;
2420 }
2421
2422 if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2423 continue;
2424 }
2425
2426 $reactivate_modules[] = $active_module;
2427 self::deactivate_module( $active_module );
2428 }
2429
2430 $new_version = JETPACK__VERSION . ':' . time();
2431 /** This action is documented in class.jetpack.php */
2432 do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2433 Jetpack_Options::update_options(
2434 array(
2435 'version' => $new_version,
2436 'old_version' => $jetpack_old_version,
2437 )
2438 );
2439
2440 self::state( 'message', 'modules_activated' );
2441
2442 self::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules, $redirect );
2443
2444 if ( $redirect ) {
2445 $page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2446 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2447 $page = $_GET['page'];
2448 }
2449
2450 wp_safe_redirect( self::admin_url( 'page=' . $page ) );
2451 exit;
2452 }
2453 }
2454
2455 /**
2456 * List available Jetpack modules. Simply lists .php files in /modules/.
2457 * Make sure to tuck away module "library" files in a sub-directory.
2458 *
2459 * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter.
2460 * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter.
2461 * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection.
2462 * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection.
2463 *
2464 * @return array $modules Array of module slugs
2465 */
2466 public static function get_available_modules( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) {
2467 static $modules = null;
2468
2469 if ( ! isset( $modules ) ) {
2470 $available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2471 // Use the cache if we're on the front-end and it's available...
2472 if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2473 $modules = $available_modules_option[ JETPACK__VERSION ];
2474 } else {
2475 $files = self::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2476
2477 $modules = array();
2478
2479 foreach ( $files as $file ) {
2480 $slug = self::get_module_slug( $file );
2481 $headers = self::get_module( $slug );
2482
2483 if ( ! $headers ) {
2484 continue;
2485 }
2486
2487 $modules[ $slug ] = $headers['introduced'];
2488 }
2489
2490 Jetpack_Options::update_option(
2491 'available_modules',
2492 array(
2493 JETPACK__VERSION => $modules,
2494 )
2495 );
2496 }
2497 }
2498
2499 /**
2500 * Filters the array of modules available to be activated.
2501 *
2502 * @since 2.4.0
2503 *
2504 * @param array $modules Array of available modules.
2505 * @param string $min_version Minimum version number required to use modules.
2506 * @param string $max_version Maximum version number required to use modules.
2507 * @param bool|null $requires_connection Value of the Requires Connection filter.
2508 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
2509 */
2510 $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version, $requires_connection, $requires_user_connection );
2511
2512 if ( ! $min_version && ! $max_version && is_null( $requires_connection ) && is_null( $requires_user_connection ) ) {
2513 return array_keys( $mods );
2514 }
2515
2516 $r = array();
2517 foreach ( $mods as $slug => $introduced ) {
2518 if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2519 continue;
2520 }
2521
2522 if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2523 continue;
2524 }
2525
2526 $mod_details = self::get_module( $slug );
2527
2528 if ( null !== $requires_connection && (bool) $requires_connection !== $mod_details['requires_connection'] ) {
2529 continue;
2530 }
2531
2532 if ( null !== $requires_user_connection && (bool) $requires_user_connection !== $mod_details['requires_user_connection'] ) {
2533 continue;
2534 }
2535
2536 $r[] = $slug;
2537 }
2538
2539 return $r;
2540 }
2541
2542 /**
2543 * Get default modules loaded on activation.
2544 *
2545 * @param bool|string $min_version Onlu return modules introduced in this version or later. Default is false, do not filter.
2546 * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter.
2547 * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection.
2548 * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection.
2549 *
2550 * @return array $modules Array of module slugs
2551 */
2552 public static function get_default_modules( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) {
2553 $return = array();
2554
2555 foreach ( self::get_available_modules( $min_version, $max_version, $requires_connection, $requires_user_connection ) as $module ) {
2556 $module_data = self::get_module( $module );
2557
2558 switch ( strtolower( $module_data['auto_activate'] ) ) {
2559 case 'yes':
2560 $return[] = $module;
2561 break;
2562 case 'public':
2563 if ( Jetpack_Options::get_option( 'public' ) ) {
2564 $return[] = $module;
2565 }
2566 break;
2567 case 'no':
2568 default:
2569 break;
2570 }
2571 }
2572 /**
2573 * Filters the array of default modules.
2574 *
2575 * @since 2.5.0
2576 *
2577 * @param array $return Array of default modules.
2578 * @param string $min_version Minimum version number required to use modules.
2579 * @param string $max_version Maximum version number required to use modules.
2580 * @param bool|null $requires_connection Value of the Requires Connection filter.
2581 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
2582 */
2583 return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version, $requires_connection, $requires_user_connection );
2584 }
2585
2586 /**
2587 * Checks activated modules during auto-activation to determine
2588 * if any of those modules are being deprecated. If so, close
2589 * them out, and add any replacement modules.
2590 *
2591 * Runs at priority 99 by default.
2592 *
2593 * This is run late, so that it can still activate a module if
2594 * the new module is a replacement for another that the user
2595 * currently has active, even if something at the normal priority
2596 * would kibosh everything.
2597 *
2598 * @since 2.6
2599 * @uses jetpack_get_default_modules filter
2600 * @param array $modules
2601 * @return array
2602 */
2603 function handle_deprecated_modules( $modules ) {
2604 $deprecated_modules = array(
2605 'debug' => null, // Closed out and moved to the debugger library.
2606 'wpcc' => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2607 'gplus-authorship' => null, // Closed out in 3.2 -- Google dropped support.
2608 'minileven' => null, // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2609 );
2610
2611 // Don't activate SSO if they never completed activating WPCC.
2612 if ( self::is_module_active( 'wpcc' ) ) {
2613 $wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2614 if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2615 $deprecated_modules['wpcc'] = null;
2616 }
2617 }
2618
2619 foreach ( $deprecated_modules as $module => $replacement ) {
2620 if ( self::is_module_active( $module ) ) {
2621 self::deactivate_module( $module );
2622 if ( $replacement ) {
2623 $modules[] = $replacement;
2624 }
2625 }
2626 }
2627
2628 return array_unique( $modules );
2629 }
2630
2631 /**
2632 * Checks activated plugins during auto-activation to determine
2633 * if any of those plugins are in the list with a corresponding module
2634 * that is not compatible with the plugin. The module will not be allowed
2635 * to auto-activate.
2636 *
2637 * @since 2.6
2638 * @uses jetpack_get_default_modules filter
2639 * @param array $modules
2640 * @return array
2641 */
2642 function filter_default_modules( $modules ) {
2643
2644 $active_plugins = self::get_active_plugins();
2645
2646 if ( ! empty( $active_plugins ) ) {
2647
2648 // For each module we'd like to auto-activate...
2649 foreach ( $modules as $key => $module ) {
2650 // If there are potential conflicts for it...
2651 if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2652 // For each potential conflict...
2653 foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2654 // If that conflicting plugin is active...
2655 if ( in_array( $plugin, $active_plugins ) ) {
2656 // Remove that item from being auto-activated.
2657 unset( $modules[ $key ] );
2658 }
2659 }
2660 }
2661 }
2662 }
2663
2664 return $modules;
2665 }
2666
2667 /**
2668 * Extract a module's slug from its full path.
2669 */
2670 public static function get_module_slug( $file ) {
2671 return str_replace( '.php', '', basename( $file ) );
2672 }
2673
2674 /**
2675 * Generate a module's path from its slug.
2676 */
2677 public static function get_module_path( $slug ) {
2678 /**
2679 * Filters the path of a modules.
2680 *
2681 * @since 7.4.0
2682 *
2683 * @param array $return The absolute path to a module's root php file
2684 * @param string $slug The module slug
2685 */
2686 return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
2687 }
2688
2689 /**
2690 * Load module data from module file. Headers differ from WordPress
2691 * plugin headers to avoid them being identified as standalone
2692 * plugins on the WordPress plugins page.
2693 *
2694 * @param string $module The module slug.
2695 */
2696 public static function get_module( $module ) {
2697 static $modules_details;
2698
2699 if ( jetpack_has_no_module_info( $module ) ) {
2700 return false;
2701 }
2702
2703 $file = self::get_module_path( self::get_module_slug( $module ) );
2704
2705 if ( isset( $modules_details[ $module ] ) ) {
2706 $mod = $modules_details[ $module ];
2707 } else {
2708 $mod = jetpack_get_module_info( $module );
2709
2710 if ( null === $mod ) {
2711 // Try to get the module info from the file as a fallback.
2712 $mod = self::get_file_data( $file, jetpack_get_all_module_header_names() );
2713
2714 if ( empty( $mod['name'] ) ) {
2715 // No info for this module.
2716 return false;
2717 }
2718 }
2719
2720 $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2721 $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2722 $mod['deactivate'] = empty( $mod['deactivate'] );
2723 $mod['free'] = empty( $mod['free'] );
2724 $mod['requires_connection'] = ( ! empty( $mod['requires_connection'] ) && 'No' === $mod['requires_connection'] ) ? false : true;
2725 $mod['requires_user_connection'] = ( empty( $mod['requires_user_connection'] ) || 'No' === $mod['requires_user_connection'] ) ? false : true;
2726
2727 if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ), true ) ) {
2728 $mod['auto_activate'] = 'No';
2729 } else {
2730 $mod['auto_activate'] = (string) $mod['auto_activate'];
2731 }
2732
2733 if ( $mod['module_tags'] ) {
2734 $mod['module_tags'] = explode( ',', $mod['module_tags'] );
2735 $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2736 $mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2737 } else {
2738 $mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2739 }
2740
2741 if ( $mod['plan_classes'] ) {
2742 $mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2743 $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2744 } else {
2745 $mod['plan_classes'] = array( 'free' );
2746 }
2747
2748 if ( $mod['feature'] ) {
2749 $mod['feature'] = explode( ',', $mod['feature'] );
2750 $mod['feature'] = array_map( 'trim', $mod['feature'] );
2751 } else {
2752 $mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2753 }
2754
2755 $modules_details[ $module ] = $mod;
2756
2757 }
2758
2759 /**
2760 * Filters the feature array on a module.
2761 *
2762 * This filter allows you to control where each module is filtered: Recommended,
2763 * and the default "Other" listing.
2764 *
2765 * @since 3.5.0
2766 *
2767 * @param array $mod['feature'] The areas to feature this module:
2768 * 'Recommended' shows on the main Jetpack admin screen.
2769 * 'Other' should be the default if no other value is in the array.
2770 * @param string $module The slug of the module, e.g. sharedaddy.
2771 * @param array $mod All the currently assembled module data.
2772 */
2773 $mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2774
2775 /**
2776 * Filter the returned data about a module.
2777 *
2778 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2779 * so please be careful.
2780 *
2781 * @since 3.6.0
2782 *
2783 * @param array $mod The details of the requested module.
2784 * @param string $module The slug of the module, e.g. sharedaddy
2785 * @param string $file The path to the module source file.
2786 */
2787 return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2788 }
2789
2790 /**
2791 * Like core's get_file_data implementation, but caches the result.
2792 */
2793 public static function get_file_data( $file, $headers ) {
2794 // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2795 $file_name = basename( $file );
2796
2797 $cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2798
2799 $file_data_option = get_transient( $cache_key );
2800
2801 if ( ! is_array( $file_data_option ) ) {
2802 delete_transient( $cache_key );
2803 $file_data_option = false;
2804 }
2805
2806 if ( false === $file_data_option ) {
2807 $file_data_option = array();
2808 }
2809
2810 $key = md5( $file_name . serialize( $headers ) );
2811 $refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2812
2813 // If we don't need to refresh the cache, and already have the value, short-circuit!
2814 if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2815 return $file_data_option[ $key ];
2816 }
2817
2818 $data = get_file_data( $file, $headers );
2819
2820 $file_data_option[ $key ] = $data;
2821
2822 set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2823
2824 return $data;
2825 }
2826
2827 /**
2828 * Return translated module tag.
2829 *
2830 * @param string $tag Tag as it appears in each module heading.
2831 *
2832 * @return mixed
2833 */
2834 public static function translate_module_tag( $tag ) {
2835 return jetpack_get_module_i18n_tag( $tag );
2836 }
2837
2838 /**
2839 * Return module name translation. Uses matching string created in modules/module-headings.php.
2840 *
2841 * @since 3.9.2
2842 *
2843 * @param array $modules
2844 *
2845 * @return string|void
2846 */
2847 public static function get_translated_modules( $modules ) {
2848 foreach ( $modules as $index => $module ) {
2849 $i18n_module = jetpack_get_module_i18n( $module['module'] );
2850 if ( isset( $module['name'] ) ) {
2851 $modules[ $index ]['name'] = $i18n_module['name'];
2852 }
2853 if ( isset( $module['description'] ) ) {
2854 $modules[ $index ]['description'] = $i18n_module['description'];
2855 $modules[ $index ]['short_description'] = $i18n_module['description'];
2856 }
2857 }
2858 return $modules;
2859 }
2860
2861 /**
2862 * Get a list of activated modules as an array of module slugs.
2863 */
2864 public static function get_active_modules() {
2865 $active = Jetpack_Options::get_option( 'active_modules' );
2866
2867 if ( ! is_array( $active ) ) {
2868 $active = array();
2869 }
2870
2871 if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2872 $active[] = 'vaultpress';
2873 } else {
2874 $active = array_diff( $active, array( 'vaultpress' ) );
2875 }
2876
2877 // If protect is active on the main site of a multisite, it should be active on all sites.
2878 if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2879 $active[] = 'protect';
2880 }
2881
2882 /**
2883 * Allow filtering of the active modules.
2884 *
2885 * Gives theme and plugin developers the power to alter the modules that
2886 * are activated on the fly.
2887 *
2888 * @since 5.8.0
2889 *
2890 * @param array $active Array of active module slugs.
2891 */
2892 $active = apply_filters( 'jetpack_active_modules', $active );
2893
2894 return array_unique( $active );
2895 }
2896
2897 /**
2898 * Check whether or not a Jetpack module is active.
2899 *
2900 * @param string $module The slug of a Jetpack module.
2901 * @return bool
2902 *
2903 * @static
2904 */
2905 public static function is_module_active( $module ) {
2906 return in_array( $module, self::get_active_modules() );
2907 }
2908
2909 public static function is_module( $module ) {
2910 return ! empty( $module ) && ! validate_file( $module, self::get_available_modules() );
2911 }
2912
2913 /**
2914 * Catches PHP errors. Must be used in conjunction with output buffering.
2915 *
2916 * @param bool $catch True to start catching, False to stop.
2917 *
2918 * @static
2919 */
2920 public static function catch_errors( $catch ) {
2921 static $display_errors, $error_reporting;
2922
2923 if ( $catch ) {
2924 $display_errors = @ini_set( 'display_errors', 1 );
2925 $error_reporting = @error_reporting( E_ALL );
2926 add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2927 } else {
2928 @ini_set( 'display_errors', $display_errors );
2929 @error_reporting( $error_reporting );
2930 remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2931 }
2932 }
2933
2934 /**
2935 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2936 */
2937 public static function catch_errors_on_shutdown() {
2938 self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2939 }
2940
2941 /**
2942 * Rewrite any string to make paths easier to read.
2943 *
2944 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2945 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2946 *
2947 * @param $string
2948 * @return mixed
2949 */
2950 public static function alias_directories( $string ) {
2951 // ABSPATH has a trailing slash.
2952 $string = str_replace( ABSPATH, 'ABSPATH/', $string );
2953 // WP_CONTENT_DIR does not have a trailing slash.
2954 $string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2955
2956 return $string;
2957 }
2958
2959 public static function activate_default_modules(
2960 $min_version = false,
2961 $max_version = false,
2962 $other_modules = array(),
2963 $redirect = null,
2964 $send_state_messages = null,
2965 $requires_connection = null,
2966 $requires_user_connection = null
2967 ) {
2968 $jetpack = self::init();
2969
2970 if ( is_null( $redirect ) ) {
2971 if (
2972 ( defined( 'REST_REQUEST' ) && REST_REQUEST )
2973 ||
2974 ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
2975 ||
2976 ( defined( 'WP_CLI' ) && WP_CLI )
2977 ||
2978 ( defined( 'DOING_CRON' ) && DOING_CRON )
2979 ||
2980 ( defined( 'DOING_AJAX' ) && DOING_AJAX )
2981 ) {
2982 $redirect = false;
2983 } elseif ( is_admin() ) {
2984 $redirect = true;
2985 } else {
2986 $redirect = false;
2987 }
2988 }
2989
2990 if ( is_null( $send_state_messages ) ) {
2991 $send_state_messages = current_user_can( 'jetpack_activate_modules' );
2992 }
2993
2994 $modules = self::get_default_modules( $min_version, $max_version, $requires_connection, $requires_user_connection );
2995 $modules = array_merge( $other_modules, $modules );
2996
2997 // Look for standalone plugins and disable if active.
2998
2999 $to_deactivate = array();
3000 foreach ( $modules as $module ) {
3001 if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3002 $to_deactivate[ $module ] = $jetpack->plugins_to_deactivate[ $module ];
3003 }
3004 }
3005
3006 $deactivated = array();
3007 foreach ( $to_deactivate as $module => $deactivate_me ) {
3008 list( $probable_file, $probable_title ) = $deactivate_me;
3009 if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
3010 $deactivated[] = $module;
3011 }
3012 }
3013
3014 if ( $deactivated ) {
3015 if ( $send_state_messages ) {
3016 self::state( 'deactivated_plugins', join( ',', $deactivated ) );
3017 }
3018
3019 if ( $redirect ) {
3020 $url = add_query_arg(
3021 array(
3022 'action' => 'activate_default_modules',
3023 '_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
3024 ),
3025 add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
3026 );
3027 wp_safe_redirect( $url );
3028 exit;
3029 }
3030 }
3031
3032 /**
3033 * Fires before default modules are activated.
3034 *
3035 * @since 1.9.0
3036 *
3037 * @param string $min_version Minimum version number required to use modules.
3038 * @param string $max_version Maximum version number required to use modules.
3039 * @param array $other_modules Array of other modules to activate alongside the default modules.
3040 * @param bool|null $requires_connection Value of the Requires Connection filter.
3041 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
3042 */
3043 do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules, $requires_connection, $requires_user_connection );
3044
3045 // Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
3046 if ( $send_state_messages ) {
3047 self::restate();
3048 self::catch_errors( true );
3049 }
3050
3051 $active = self::get_active_modules();
3052
3053 foreach ( $modules as $module ) {
3054 if ( did_action( "jetpack_module_loaded_$module" ) ) {
3055 $active[] = $module;
3056 self::update_active_modules( $active );
3057 continue;
3058 }
3059
3060 if ( $send_state_messages && in_array( $module, $active ) ) {
3061 $module_info = self::get_module( $module );
3062 if ( ! $module_info['deactivate'] ) {
3063 $state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
3064 if ( $active_state = self::state( $state ) ) {
3065 $active_state = explode( ',', $active_state );
3066 } else {
3067 $active_state = array();
3068 }
3069 $active_state[] = $module;
3070 self::state( $state, implode( ',', $active_state ) );
3071 }
3072 continue;
3073 }
3074
3075 $file = self::get_module_path( $module );
3076 if ( ! file_exists( $file ) ) {
3077 continue;
3078 }
3079
3080 // we'll override this later if the plugin can be included without fatal error
3081 if ( $redirect ) {
3082 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3083 }
3084
3085 if ( $send_state_messages ) {
3086 self::state( 'error', 'module_activation_failed' );
3087 self::state( 'module', $module );
3088 }
3089
3090 ob_start();
3091 require_once $file;
3092
3093 $active[] = $module;
3094
3095 if ( $send_state_messages ) {
3096
3097 $state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
3098 if ( $active_state = self::state( $state ) ) {
3099 $active_state = explode( ',', $active_state );
3100 } else {
3101 $active_state = array();
3102 }
3103 $active_state[] = $module;
3104 self::state( $state, implode( ',', $active_state ) );
3105 }
3106
3107 self::update_active_modules( $active );
3108
3109 ob_end_clean();
3110 }
3111
3112 if ( $send_state_messages ) {
3113 self::state( 'error', false );
3114 self::state( 'module', false );
3115 }
3116
3117 self::catch_errors( false );
3118 /**
3119 * Fires when default modules are activated.
3120 *
3121 * @since 1.9.0
3122 *
3123 * @param string $min_version Minimum version number required to use modules.
3124 * @param string $max_version Maximum version number required to use modules.
3125 * @param array $other_modules Array of other modules to activate alongside the default modules.
3126 * @param bool|null $requires_connection Value of the Requires Connection filter.
3127 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
3128 */
3129 do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules, $requires_connection, $requires_user_connection );
3130 }
3131
3132 public static function activate_module( $module, $exit = true, $redirect = true ) {
3133 /**
3134 * Fires before a module is activated.
3135 *
3136 * @since 2.6.0
3137 *
3138 * @param string $module Module slug.
3139 * @param bool $exit Should we exit after the module has been activated. Default to true.
3140 * @param bool $redirect Should the user be redirected after module activation? Default to true.
3141 */
3142 do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
3143
3144 $jetpack = self::init();
3145
3146 if ( ! strlen( $module ) ) {
3147 return false;
3148 }
3149
3150 if ( ! self::is_module( $module ) ) {
3151 return false;
3152 }
3153
3154 // If it's already active, then don't do it again
3155 $active = self::get_active_modules();
3156 foreach ( $active as $act ) {
3157 if ( $act == $module ) {
3158 return true;
3159 }
3160 }
3161
3162 $module_data = self::get_module( $module );
3163
3164 $is_offline_mode = ( new Status() )->is_offline_mode();
3165 if ( ! self::is_connection_ready() ) {
3166 if ( ! $is_offline_mode && ! self::is_onboarding() ) {
3167 return false;
3168 }
3169
3170 // If we're not connected but in offline mode, make sure the module doesn't require a connection.
3171 if ( $is_offline_mode && $module_data['requires_connection'] ) {
3172 return false;
3173 }
3174 }
3175
3176 // Check and see if the old plugin is active
3177 if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3178 // Deactivate the old plugin
3179 if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
3180 // If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
3181 // We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
3182 self::state( 'deactivated_plugins', $module );
3183 wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
3184 exit;
3185 }
3186 }
3187
3188 // Protect won't work with mis-configured IPs
3189 if ( 'protect' === $module ) {
3190 include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
3191 if ( ! jetpack_protect_get_ip() ) {
3192 self::state( 'message', 'protect_misconfigured_ip' );
3193 return false;
3194 }
3195 }
3196
3197 if ( ! Jetpack_Plan::supports( $module ) ) {
3198 return false;
3199 }
3200
3201 // Check the file for fatal errors, a la wp-admin/plugins.php::activate
3202 self::state( 'module', $module );
3203 self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
3204
3205 self::catch_errors( true );
3206 ob_start();
3207 require self::get_module_path( $module );
3208 /** This action is documented in class.jetpack.php */
3209 do_action( 'jetpack_activate_module', $module );
3210 $active[] = $module;
3211 self::update_active_modules( $active );
3212
3213 self::state( 'error', false ); // the override
3214 ob_end_clean();
3215 self::catch_errors( false );
3216
3217 if ( $redirect ) {
3218 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3219 }
3220 if ( $exit ) {
3221 exit;
3222 }
3223 return true;
3224 }
3225
3226 function activate_module_actions( $module ) {
3227 _deprecated_function( __METHOD__, 'jetpack-4.2' );
3228 }
3229
3230 public static function deactivate_module( $module ) {
3231 /**
3232 * Fires when a module is deactivated.
3233 *
3234 * @since 1.9.0
3235 *
3236 * @param string $module Module slug.
3237 */
3238 do_action( 'jetpack_pre_deactivate_module', $module );
3239
3240 $jetpack = self::init();
3241
3242 $active = self::get_active_modules();
3243 $new = array_filter( array_diff( $active, (array) $module ) );
3244
3245 return self::update_active_modules( $new );
3246 }
3247
3248 public static function enable_module_configurable( $module ) {
3249 $module = self::get_module_slug( $module );
3250 add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3251 }
3252
3253 /**
3254 * Composes a module configure URL. It uses Jetpack settings search as default value
3255 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3256 *
3257 * @param string $module Module slug
3258 * @return string $url module configuration URL
3259 */
3260 public static function module_configuration_url( $module ) {
3261 $module = self::get_module_slug( $module );
3262 $default_url = self::admin_url() . "#/settings?term=$module";
3263 /**
3264 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3265 *
3266 * @since 6.9.0
3267 *
3268 * @param string $default_url Default url, which redirects to jetpack settings page.
3269 */
3270 $url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3271
3272 return $url;
3273 }
3274
3275 /* Installation */
3276 public static function bail_on_activation( $message, $deactivate = true ) {
3277 ?>
3278 <!doctype html>
3279 <html>
3280 <head>
3281 <meta charset="<?php bloginfo( 'charset' ); ?>">
3282 <style>
3283 * {
3284 text-align: center;
3285 margin: 0;
3286 padding: 0;
3287 font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3288 }
3289 p {
3290 margin-top: 1em;
3291 font-size: 18px;
3292 }
3293 </style>
3294 <body>
3295 <p><?php echo esc_html( $message ); ?></p>
3296 </body>
3297 </html>
3298 <?php
3299 if ( $deactivate ) {
3300 $plugins = get_option( 'active_plugins' );
3301 $jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3302 $update = false;
3303 foreach ( $plugins as $i => $plugin ) {
3304 if ( $plugin === $jetpack ) {
3305 $plugins[ $i ] = false;
3306 $update = true;
3307 }
3308 }
3309
3310 if ( $update ) {
3311 update_option( 'active_plugins', array_filter( $plugins ) );
3312 }
3313 }
3314 exit;
3315 }
3316
3317 /**
3318 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3319 *
3320 * @static
3321 */
3322 public static function plugin_activation( $network_wide ) {
3323 Jetpack_Options::update_option( 'activated', 1 );
3324
3325 if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3326 self::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3327 }
3328
3329 if ( $network_wide ) {
3330 self::state( 'network_nag', true );
3331 }
3332
3333 // For firing one-off events (notices) immediately after activation
3334 set_transient( 'activated_jetpack', true, 0.1 * MINUTE_IN_SECONDS );
3335
3336 update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3337
3338 Health::on_jetpack_activated();
3339
3340 self::plugin_initialize();
3341 }
3342
3343 public static function get_activation_source( $referer_url ) {
3344
3345 if ( defined( 'WP_CLI' ) && WP_CLI ) {
3346 return array( 'wp-cli', null );
3347 }
3348
3349 $referer = wp_parse_url( $referer_url );
3350
3351 $source_type = 'unknown';
3352 $source_query = null;
3353
3354 if ( ! is_array( $referer ) ) {
3355 return array( $source_type, $source_query );
3356 }
3357
3358 $plugins_path = wp_parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
3359 $plugins_install_path = wp_parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
3360
3361 if ( isset( $referer['query'] ) ) {
3362 parse_str( $referer['query'], $query_parts );
3363 } else {
3364 $query_parts = array();
3365 }
3366
3367 if ( $plugins_path === $referer['path'] ) {
3368 $source_type = 'list';
3369 } elseif ( $plugins_install_path === $referer['path'] ) {
3370 $tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3371 switch ( $tab ) {
3372 case 'popular':
3373 $source_type = 'popular';
3374 break;
3375 case 'recommended':
3376 $source_type = 'recommended';
3377 break;
3378 case 'favorites':
3379 $source_type = 'favorites';
3380 break;
3381 case 'search':
3382 $source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3383 $source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3384 break;
3385 default:
3386 $source_type = 'featured';
3387 }
3388 }
3389
3390 return array( $source_type, $source_query );
3391 }
3392
3393 /**
3394 * Runs before bumping version numbers up to a new version
3395 *
3396 * @param string $version Version:timestamp.
3397 * @param string $old_version Old Version:timestamp or false if not set yet.
3398 */
3399 public static function do_version_bump( $version, $old_version ) {
3400 if ( $old_version ) { // For existing Jetpack installations.
3401 add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3402
3403 // If a front end page is visited after the update, the 'wp' action will fire.
3404 add_action( 'wp', 'Jetpack::set_update_modal_display' );
3405
3406 // If an admin page is visited after the update, the 'current_screen' action will fire.
3407 add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3408 }
3409 }
3410
3411 /**
3412 * Sets the display_update_modal state.
3413 */
3414 public static function set_update_modal_display() {
3415 self::state( 'display_update_modal', true );
3416
3417 }
3418
3419 /**
3420 * Enqueues the block library styles.
3421 *
3422 * @param string $hook The current admin page.
3423 */
3424 public static function enqueue_block_style( $hook ) {
3425 if ( 'toplevel_page_jetpack' === $hook ) {
3426 wp_enqueue_style( 'wp-block-library' );
3427 }
3428 }
3429
3430 /**
3431 * Sets the internal version number and activation state.
3432 *
3433 * @static
3434 */
3435 public static function plugin_initialize() {
3436 if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3437 Jetpack_Options::update_option( 'activated', 2 );
3438 }
3439
3440 if ( ! Jetpack_Options::get_option( 'version' ) ) {
3441 $version = $old_version = JETPACK__VERSION . ':' . time();
3442 /** This action is documented in class.jetpack.php */
3443 do_action( 'updating_jetpack_version', $version, false );
3444 Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3445 }
3446
3447 self::load_modules();
3448
3449 Jetpack_Options::delete_option( 'do_activate' );
3450 Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3451 }
3452
3453 /**
3454 * Removes all connection options
3455 *
3456 * @static
3457 */
3458 public static function plugin_deactivation() {
3459 require_once ABSPATH . '/wp-admin/includes/plugin.php';
3460 $tracking = new Tracking();
3461 $tracking->record_user_event( 'deactivate_plugin', array() );
3462 if ( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3463 Jetpack_Network::init()->deactivate();
3464 } else {
3465 add_filter( 'jetpack_update_activated_state_on_disconnect', '__return_false' );
3466 self::disconnect();
3467 // Jetpack_Heartbeat::init()->deactivate();
3468 }
3469 }
3470
3471 /**
3472 * Set activated option to 4 on jetpack_idc_disconnect action.
3473 */
3474 public static function on_idc_disconnect() {
3475 \Jetpack_Options::update_option( 'activated', 4 );
3476 }
3477
3478 /**
3479 * Disconnects from the Jetpack servers.
3480 * Forgets all connection details and tells the Jetpack servers to do the same.
3481 *
3482 * @static
3483 */
3484 public static function disconnect() {
3485
3486 $connection = self::connection();
3487
3488 // If the site is in an IDC because sync is not allowed,
3489 // let's make sure to not disconnect the production site.
3490 $connection->disconnect_site( ! Identity_Crisis::validate_sync_error_idc_option() );
3491 }
3492
3493 /**
3494 * Happens after a successfull disconnection.
3495 *
3496 * @static
3497 */
3498 public static function jetpack_site_disconnected() {
3499 Identity_Crisis::clear_all_idc_options();
3500
3501 // Delete all the sync related data. Since it could be taking up space.
3502 Sender::get_instance()->uninstall();
3503
3504 /**
3505 * Filters whether the Jetpack activated state should be updated after disconnecting.
3506 *
3507 * @since 10.0.0
3508 *
3509 * @param bool $update_activated_state Whether activated state should be updated after disconnecting, defaults to true.
3510 */
3511 $update_activated_state = apply_filters( 'jetpack_update_activated_state_on_disconnect', true );
3512
3513 if ( $update_activated_state ) {
3514 Jetpack_Options::update_option( 'activated', 4 );
3515 }
3516 }
3517
3518 /**
3519 * Disconnects the user
3520 *
3521 * @param int $user_id The user ID to disconnect.
3522 */
3523 public function disconnect_user( $user_id ) {
3524 $this->connection_manager->disconnect_user( $user_id );
3525 }
3526
3527 /**
3528 * Attempts Jetpack registration. If it fail, a state flag is set: @see ::admin_page_load()
3529 *
3530 * @deprecated since Jetpack 9.7.0
3531 * @see Automattic\Jetpack\Connection\Manager::try_registration()
3532 *
3533 * @return bool|WP_Error
3534 */
3535 public static function try_registration() {
3536 _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
3537 return static::connection()->try_registration();
3538 }
3539
3540 /**
3541 * Checking the domain names in beta versions.
3542 * If this is a development version, before attempting to connect, let's make sure that the domains are viable.
3543 *
3544 * @param null|\WP_Error $error The domain validation error, or `null` if everything's fine.
3545 *
3546 * @return null|\WP_Error The domain validation error, or `null` if everything's fine.
3547 */
3548 public static function registration_check_domains( $error ) {
3549 if ( static::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3550 $domains_to_check = array_unique(
3551 array(
3552 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3553 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
3554 )
3555 );
3556 foreach ( $domains_to_check as $domain ) {
3557 $result = static::connection()->is_usable_domain( $domain );
3558 if ( is_wp_error( $result ) ) {
3559 return $result;
3560 }
3561 }
3562 }
3563
3564 return $error;
3565 }
3566
3567 /**
3568 * Tracking an internal event log. Try not to put too much chaff in here.
3569 *
3570 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3571 */
3572 public static function log( $code, $data = null ) {
3573 // only grab the latest 200 entries
3574 $log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3575
3576 // Append our event to the log
3577 $log_entry = array(
3578 'time' => time(),
3579 'user_id' => get_current_user_id(),
3580 'blog_id' => Jetpack_Options::get_option( 'id' ),
3581 'code' => $code,
3582 );
3583 // Don't bother storing it unless we've got some.
3584 if ( ! is_null( $data ) ) {
3585 $log_entry['data'] = $data;
3586 }
3587 $log[] = $log_entry;
3588
3589 // Try add_option first, to make sure it's not autoloaded.
3590 // @todo: Add an add_option method to Jetpack_Options
3591 if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3592 Jetpack_Options::update_option( 'log', $log );
3593 }
3594
3595 /**
3596 * Fires when Jetpack logs an internal event.
3597 *
3598 * @since 3.0.0
3599 *
3600 * @param array $log_entry {
3601 * Array of details about the log entry.
3602 *
3603 * @param string time Time of the event.
3604 * @param int user_id ID of the user who trigerred the event.
3605 * @param int blog_id Jetpack Blog ID.
3606 * @param string code Unique name for the event.
3607 * @param string data Data about the event.
3608 * }
3609 */
3610 do_action( 'jetpack_log_entry', $log_entry );
3611 }
3612
3613 /**
3614 * Get the internal event log.
3615 *
3616 * @param $event (string) - only return the specific log events
3617 * @param $num (int) - get specific number of latest results, limited to 200
3618 *
3619 * @return array of log events || WP_Error for invalid params
3620 */
3621 public static function get_log( $event = false, $num = false ) {
3622 if ( $event && ! is_string( $event ) ) {
3623 return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
3624 }
3625
3626 if ( $num && ! is_numeric( $num ) ) {
3627 return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
3628 }
3629
3630 $entire_log = Jetpack_Options::get_option( 'log', array() );
3631
3632 // If nothing set - act as it did before, otherwise let's start customizing the output
3633 if ( ! $num && ! $event ) {
3634 return $entire_log;
3635 } else {
3636 $entire_log = array_reverse( $entire_log );
3637 }
3638
3639 $custom_log_output = array();
3640
3641 if ( $event ) {
3642 foreach ( $entire_log as $log_event ) {
3643 if ( $event == $log_event['code'] ) {
3644 $custom_log_output[] = $log_event;
3645 }
3646 }
3647 } else {
3648 $custom_log_output = $entire_log;
3649 }
3650
3651 if ( $num ) {
3652 $custom_log_output = array_slice( $custom_log_output, 0, $num );
3653 }
3654
3655 return $custom_log_output;
3656 }
3657
3658 /**
3659 * Log modification of important settings.
3660 */
3661 public static function log_settings_change( $option, $old_value, $value ) {
3662 switch ( $option ) {
3663 case 'jetpack_sync_non_public_post_stati':
3664 self::log( $option, $value );
3665 break;
3666 }
3667 }
3668
3669 /**
3670 * Return stat data for WPCOM sync
3671 */
3672 public static function get_stat_data( $encode = true, $extended = true ) {
3673 $data = Jetpack_Heartbeat::generate_stats_array();
3674
3675 if ( $extended ) {
3676 $additional_data = self::get_additional_stat_data();
3677 $data = array_merge( $data, $additional_data );
3678 }
3679
3680 if ( $encode ) {
3681 return json_encode( $data );
3682 }
3683
3684 return $data;
3685 }
3686
3687 /**
3688 * Get additional stat data to sync to WPCOM
3689 */
3690 public static function get_additional_stat_data( $prefix = '' ) {
3691 $return[ "{$prefix}themes" ] = self::get_parsed_theme_data();
3692 $return[ "{$prefix}plugins-extra" ] = self::get_parsed_plugin_data();
3693 $return[ "{$prefix}users" ] = (int) self::get_site_user_count();
3694 $return[ "{$prefix}site-count" ] = 0;
3695
3696 if ( function_exists( 'get_blog_count' ) ) {
3697 $return[ "{$prefix}site-count" ] = get_blog_count();
3698 }
3699 return $return;
3700 }
3701
3702 private static function get_site_user_count() {
3703 global $wpdb;
3704
3705 if ( function_exists( 'wp_is_large_network' ) ) {
3706 if ( wp_is_large_network( 'users' ) ) {
3707 return -1; // Not a real value but should tell us that we are dealing with a large network.
3708 }
3709 }
3710 if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3711 // It wasn't there, so regenerate the data and save the transient
3712 $user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3713 set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3714 }
3715 return $user_count;
3716 }
3717
3718 /* Admin Pages */
3719
3720 function admin_init() {
3721 // If the plugin is not connected, display a connect message.
3722 if (
3723 // the plugin was auto-activated and needs its candy
3724 Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3725 ||
3726 // the plugin is active, but was never activated. Probably came from a site-wide network activation
3727 ! Jetpack_Options::get_option( 'activated' )
3728 ) {
3729 self::plugin_initialize();
3730 }
3731
3732 $is_offline_mode = ( new Status() )->is_offline_mode();
3733 $fallback_no_verify_ssl_certs = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
3734 /** Already documented in automattic/jetpack-connection::src/class-client.php */
3735 $client_verify_ssl_certs = apply_filters( 'jetpack_client_verify_ssl_certs', false );
3736
3737 if ( ! $is_offline_mode ) {
3738 Jetpack_Connection_Banner::init();
3739 }
3740
3741 if ( ( self::is_connection_ready() || $is_offline_mode ) && false === $fallback_no_verify_ssl_certs && ! $client_verify_ssl_certs ) {
3742 // Upgrade: 1.1 -> 1.1.1
3743 // Check and see if host can verify the Jetpack servers' SSL certificate
3744 $args = array();
3745 Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3746 }
3747
3748 Jetpack_Recommendations_Banner::init();
3749
3750 if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3751 add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3752 }
3753
3754 add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3755 add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3756 add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3757
3758 if ( isset( $_COOKIE['jetpackState']['display_update_modal'] ) ) {
3759 add_action( 'admin_enqueue_scripts', __CLASS__ . '::enqueue_block_style' );
3760 }
3761
3762 add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3763
3764 if ( self::is_connection_ready() || $is_offline_mode ) {
3765 // Artificially throw errors in certain specific cases during plugin activation.
3766 add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3767 }
3768
3769 // Add custom column in wp-admin/users.php to show whether user is linked.
3770 add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3771 add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3772 add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3773 }
3774
3775 function admin_body_class( $admin_body_class = '' ) {
3776 $classes = explode( ' ', trim( $admin_body_class ) );
3777
3778 $classes[] = self::is_connection_ready() ? 'jetpack-connected' : 'jetpack-disconnected';
3779
3780 $admin_body_class = implode( ' ', array_unique( $classes ) );
3781 return " $admin_body_class ";
3782 }
3783
3784 static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3785 return $admin_body_class . ' jetpack-pagestyles ';
3786 }
3787
3788 /**
3789 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3790 * This function artificially throws errors for such cases (per a specific list).
3791 *
3792 * @param string $plugin The activated plugin.
3793 */
3794 function throw_error_on_activate_plugin( $plugin ) {
3795 $active_modules = self::get_active_modules();
3796
3797 // The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3798 if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3799 $throw = false;
3800
3801 // Try and make sure it really was the stats plugin
3802 if ( ! class_exists( 'ReflectionFunction' ) ) {
3803 if ( 'stats.php' == basename( $plugin ) ) {
3804 $throw = true;
3805 }
3806 } else {
3807 $reflection = new ReflectionFunction( 'stats_get_api_key' );
3808 if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3809 $throw = true;
3810 }
3811 }
3812
3813 if ( $throw ) {
3814 trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3815 }
3816 }
3817 }
3818
3819 function intercept_plugin_error_scrape_init() {
3820 add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3821 }
3822
3823 function intercept_plugin_error_scrape( $action, $result ) {
3824 if ( ! $result ) {
3825 return;
3826 }
3827
3828 foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3829 if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3830 self::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3831 }
3832 }
3833 }
3834
3835 /**
3836 * Register the remote file upload request handlers, if needed.
3837 *
3838 * @access public
3839 */
3840 public function add_remote_request_handlers() {
3841 // Remote file uploads are allowed only via AJAX requests.
3842 if ( ! is_admin() || ! Constants::get_constant( 'DOING_AJAX' ) ) {
3843 return;
3844 }
3845
3846 // Remote file uploads are allowed only for a set of specific AJAX actions.
3847 $remote_request_actions = array(
3848 'jetpack_upload_file',
3849 'jetpack_update_file',
3850 );
3851
3852 // phpcs:ignore WordPress.Security.NonceVerification
3853 if ( ! isset( $_POST['action'] ) || ! in_array( $_POST['action'], $remote_request_actions, true ) ) {
3854 return;
3855 }
3856
3857 // Require Jetpack authentication for the remote file upload AJAX requests.
3858 if ( ! $this->connection_manager ) {
3859 $this->connection_manager = new Connection_Manager();
3860 }
3861
3862 $this->connection_manager->require_jetpack_authentication();
3863
3864 // Register the remote file upload AJAX handlers.
3865 foreach ( $remote_request_actions as $action ) {
3866 add_action( "wp_ajax_nopriv_{$action}", array( $this, 'remote_request_handlers' ) );
3867 }
3868 }
3869
3870 /**
3871 * Handler for Jetpack remote file uploads.
3872 *
3873 * @access public
3874 */
3875 public function remote_request_handlers() {
3876 $action = current_filter();
3877
3878 switch ( current_filter() ) {
3879 case 'wp_ajax_nopriv_jetpack_upload_file':
3880 $response = $this->upload_handler();
3881 break;
3882
3883 case 'wp_ajax_nopriv_jetpack_update_file':
3884 $response = $this->upload_handler( true );
3885 break;
3886 default:
3887 $response = new WP_Error( 'unknown_handler', 'Unknown Handler', 400 );
3888 break;
3889 }
3890
3891 if ( ! $response ) {
3892 $response = new WP_Error( 'unknown_error', 'Unknown Error', 400 );
3893 }
3894
3895 if ( is_wp_error( $response ) ) {
3896 $status_code = $response->get_error_data();
3897 $error = $response->get_error_code();
3898 $error_description = $response->get_error_message();
3899
3900 if ( ! is_int( $status_code ) ) {
3901 $status_code = 400;
3902 }
3903
3904 status_header( $status_code );
3905 die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3906 }
3907
3908 status_header( 200 );
3909 if ( true === $response ) {
3910 exit;
3911 }
3912
3913 die( json_encode( (object) $response ) );
3914 }
3915
3916 /**
3917 * Uploads a file gotten from the global $_FILES.
3918 * If `$update_media_item` is true and `post_id` is defined
3919 * the attachment file of the media item (gotten through of the post_id)
3920 * will be updated instead of add a new one.
3921 *
3922 * @param boolean $update_media_item - update media attachment
3923 * @return array - An array describing the uploadind files process
3924 */
3925 function upload_handler( $update_media_item = false ) {
3926 if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3927 return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
3928 }
3929
3930 $user = wp_authenticate( '', '' );
3931 if ( ! $user || is_wp_error( $user ) ) {
3932 return new WP_Error( 403, get_status_header_desc( 403 ), 403 );
3933 }
3934
3935 wp_set_current_user( $user->ID );
3936
3937 if ( ! current_user_can( 'upload_files' ) ) {
3938 return new WP_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3939 }
3940
3941 if ( empty( $_FILES ) ) {
3942 return new WP_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3943 }
3944
3945 foreach ( array_keys( $_FILES ) as $files_key ) {
3946 if ( ! isset( $_POST[ "_jetpack_file_hmac_{$files_key}" ] ) ) {
3947 return new WP_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3948 }
3949 }
3950
3951 $media_keys = array_keys( $_FILES['media'] );
3952
3953 $token = ( new Tokens() )->get_access_token( get_current_user_id() );
3954 if ( ! $token || is_wp_error( $token ) ) {
3955 return new WP_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3956 }
3957
3958 $uploaded_files = array();
3959 $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3960 unset( $GLOBALS['post'] );
3961 foreach ( $_FILES['media']['name'] as $index => $name ) {
3962 $file = array();
3963 foreach ( $media_keys as $media_key ) {
3964 $file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ];
3965 }
3966
3967 list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][ $index ] );
3968
3969 $hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3970 if ( $hmac_provided !== $hmac_file ) {
3971 $uploaded_files[ $index ] = (object) array(
3972 'error' => 'invalid_hmac',
3973 'error_description' => 'The corresponding HMAC for this file does not match',
3974 );
3975 continue;
3976 }
3977
3978 $_FILES['.jetpack.upload.'] = $file;
3979 $post_id = isset( $_POST['post_id'][ $index ] ) ? absint( $_POST['post_id'][ $index ] ) : 0;
3980 if ( ! current_user_can( 'edit_post', $post_id ) ) {
3981 $post_id = 0;
3982 }
3983
3984 if ( $update_media_item ) {
3985 if ( ! isset( $post_id ) || $post_id === 0 ) {
3986 return new WP_Error( 'invalid_input', 'Media ID must be defined.', 400 );
3987 }
3988
3989 $media_array = $_FILES['media'];
3990
3991 $file_array['name'] = $media_array['name'][0];
3992 $file_array['type'] = $media_array['type'][0];
3993 $file_array['tmp_name'] = $media_array['tmp_name'][0];
3994 $file_array['error'] = $media_array['error'][0];
3995 $file_array['size'] = $media_array['size'][0];
3996
3997 $edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3998
3999 if ( is_wp_error( $edited_media_item ) ) {
4000 return $edited_media_item;
4001 }
4002
4003 $response = (object) array(
4004 'id' => (string) $post_id,
4005 'file' => (string) $edited_media_item->post_title,
4006 'url' => (string) wp_get_attachment_url( $post_id ),
4007 'type' => (string) $edited_media_item->post_mime_type,
4008 'meta' => (array) wp_get_attachment_metadata( $post_id ),
4009 );
4010
4011 return (array) array( $response );
4012 }
4013
4014 $attachment_id = media_handle_upload(
4015 '.jetpack.upload.',
4016 $post_id,
4017 array(),
4018 array(
4019 'action' => 'jetpack_upload_file',
4020 )
4021 );
4022
4023 if ( ! $attachment_id ) {
4024 $uploaded_files[ $index ] = (object) array(
4025 'error' => 'unknown',
4026 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site',
4027 );
4028 } elseif ( is_wp_error( $attachment_id ) ) {
4029 $uploaded_files[ $index ] = (object) array(
4030 'error' => 'attachment_' . $attachment_id->get_error_code(),
4031 'error_description' => $attachment_id->get_error_message(),
4032 );
4033 } else {
4034 $attachment = get_post( $attachment_id );
4035 $uploaded_files[ $index ] = (object) array(
4036 'id' => (string) $attachment_id,
4037 'file' => $attachment->post_title,
4038 'url' => wp_get_attachment_url( $attachment_id ),
4039 'type' => $attachment->post_mime_type,
4040 'meta' => wp_get_attachment_metadata( $attachment_id ),
4041 );
4042 }
4043 }
4044 if ( ! is_null( $global_post ) ) {
4045 $GLOBALS['post'] = $global_post;
4046 }
4047
4048 return $uploaded_files;
4049 }
4050
4051 /**
4052 * Add help to the Jetpack page
4053 *
4054 * @since Jetpack (1.2.3)
4055 * @return false if not the Jetpack page
4056 */
4057 function admin_help() {
4058 $current_screen = get_current_screen();
4059
4060 // Overview
4061 $current_screen->add_help_tab(
4062 array(
4063 'id' => 'home',
4064 'title' => __( 'Home', 'jetpack' ),
4065 'content' =>
4066 '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
4067 '<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
4068 '<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>',
4069 )
4070 );
4071
4072 // Screen Content
4073 if ( current_user_can( 'manage_options' ) ) {
4074 $current_screen->add_help_tab(
4075 array(
4076 'id' => 'settings',
4077 'title' => __( 'Settings', 'jetpack' ),
4078 'content' =>
4079 '<p><strong>' . __( 'Jetpack', 'jetpack' ) . '</strong></p>' .
4080 '<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
4081 '<ol>' .
4082 '<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
4083 '<li>' . __( 'Using the checkboxes next to each module, you can select multiple modules to toggle via the Bulk Actions menu at the top of the list.', 'jetpack' ) . '</li>' .
4084 '</ol>' .
4085 '<p>' . __( 'Using the tools on the right, you can search for specific modules, filter by module categories or which are active, or change the sorting order.', 'jetpack' ) . '</p>',
4086 )
4087 );
4088 }
4089
4090 // Help Sidebar
4091 $support_url = Redirect::get_url( 'jetpack-support' );
4092 $faq_url = Redirect::get_url( 'jetpack-faq' );
4093 $current_screen->set_help_sidebar(
4094 '<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
4095 '<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
4096 '<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
4097 '<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
4098 );
4099 }
4100
4101 function admin_menu_css() {
4102 wp_enqueue_style( 'jetpack-icons' );
4103 }
4104
4105 function admin_menu_order() {
4106 return true;
4107 }
4108
4109 function jetpack_menu_order( $menu_order ) {
4110 $jp_menu_order = array();
4111
4112 foreach ( $menu_order as $index => $item ) {
4113 if ( $item != 'jetpack' ) {
4114 $jp_menu_order[] = $item;
4115 }
4116
4117 if ( $index == 0 ) {
4118 $jp_menu_order[] = 'jetpack';
4119 }
4120 }
4121
4122 return $jp_menu_order;
4123 }
4124
4125 function admin_banner_styles() {
4126 $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
4127
4128 if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
4129 wp_register_style(
4130 'jetpack-dops-style',
4131 plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
4132 array(),
4133 JETPACK__VERSION
4134 );
4135 }
4136
4137 wp_enqueue_style(
4138 'jetpack',
4139 plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
4140 array( 'jetpack-dops-style' ),
4141 JETPACK__VERSION . '-20121016'
4142 );
4143 wp_style_add_data( 'jetpack', 'rtl', 'replace' );
4144 wp_style_add_data( 'jetpack', 'suffix', $min );
4145 }
4146
4147 function plugin_action_links( $actions ) {
4148
4149 $jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), __( 'My Jetpack', 'jetpack' ) ) );
4150
4151 if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_connection_ready() || ( new Status() )->is_offline_mode() ) ) {
4152 return array_merge(
4153 $jetpack_home,
4154 array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
4155 array( 'support' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack-debugger ' ), __( 'Support', 'jetpack' ) ) ),
4156 $actions
4157 );
4158 }
4159
4160 return array_merge( $jetpack_home, $actions );
4161 }
4162
4163 /**
4164 * Adds the deactivation warning modal if there are other active plugins using the connection
4165 *
4166 * @param string $hook The current admin page.
4167 *
4168 * @return void
4169 */
4170 public function deactivate_dialog( $hook ) {
4171 if (
4172 'plugins.php' === $hook
4173 && self::is_connection_ready()
4174 ) {
4175
4176 $active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4177
4178 if ( count( $active_plugins_using_connection ) > 1 ) {
4179
4180 add_thickbox();
4181
4182 // Register jp-tracks-functions dependency.
4183 Tracking::register_tracks_functions_scripts();
4184
4185 wp_enqueue_script(
4186 'jetpack-deactivate-dialog-js',
4187 Assets::get_file_url_for_environment(
4188 '_inc/build/jetpack-deactivate-dialog.min.js',
4189 '_inc/jetpack-deactivate-dialog.js'
4190 ),
4191 array( 'jquery', 'jp-tracks-functions' ),
4192 JETPACK__VERSION,
4193 true
4194 );
4195
4196 wp_localize_script(
4197 'jetpack-deactivate-dialog-js',
4198 'deactivate_dialog',
4199 array(
4200 'title' => __( 'Deactivate Jetpack', 'jetpack' ),
4201 'deactivate_label' => __( 'Disconnect and Deactivate', 'jetpack' ),
4202 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
4203 )
4204 );
4205
4206 add_action( 'admin_footer', array( $this, 'deactivate_dialog_content' ) );
4207
4208 wp_enqueue_style( 'jetpack-deactivate-dialog', plugins_url( 'css/jetpack-deactivate-dialog.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
4209 }
4210 }
4211 }
4212
4213 /**
4214 * Outputs the content of the deactivation modal
4215 *
4216 * @return void
4217 */
4218 public function deactivate_dialog_content() {
4219 $active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4220 unset( $active_plugins_using_connection['jetpack'] );
4221 $this->load_view( 'admin/deactivation-dialog.php', $active_plugins_using_connection );
4222 }
4223
4224 /**
4225 * Filters the login URL to include the registration flow in case the user isn't logged in.
4226 *
4227 * @param string $login_url The wp-login URL.
4228 * @param string $redirect URL to redirect users after logging in.
4229 * @since Jetpack 8.4
4230 * @return string
4231 */
4232 public function login_url( $login_url, $redirect ) {
4233 parse_str( wp_parse_url( $redirect, PHP_URL_QUERY ), $redirect_parts );
4234 if ( ! empty( $redirect_parts[ self::$jetpack_redirect_login ] ) ) {
4235 $login_url = add_query_arg( self::$jetpack_redirect_login, 'true', $login_url );
4236 }
4237 return $login_url;
4238 }
4239
4240 /**
4241 * Redirects non-authenticated users to authenticate with Calypso if redirect flag is set.
4242 *
4243 * @since Jetpack 8.4
4244 */
4245 public function login_init() {
4246 // phpcs:ignore WordPress.Security.NonceVerification
4247 if ( ! empty( $_GET[ self::$jetpack_redirect_login ] ) ) {
4248 add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4249 wp_safe_redirect(
4250 add_query_arg(
4251 array(
4252 'forceInstall' => 1,
4253 'url' => rawurlencode( get_site_url() ),
4254 ),
4255 // @todo provide way to go to specific calypso env.
4256 self::get_calypso_host() . 'jetpack/connect'
4257 )
4258 );
4259 exit;
4260 }
4261 }
4262
4263 /*
4264 * Registration flow:
4265 * 1 - ::admin_page_load() action=register
4266 * 2 - ::try_registration()
4267 * 3 - ::register()
4268 * - Creates jetpack_register option containing two secrets and a timestamp
4269 * - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4270 * siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4271 * - That request to jetpack.wordpress.com does not immediately respond. It first makes a request BACK to this site's
4272 * xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4273 * - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4274 * - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4275 * jetpack_id, jetpack_secret, jetpack_public
4276 * - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4277 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4278 * 5 - user logs in with WP.com account
4279 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4280 * - Manager::authorize()
4281 * - Manager::get_token()
4282 * - GET https://jetpack.wordpress.com/jetpack.token/1/ with
4283 * client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4284 * - which responds with access_token, token_type, scope
4285 * - Manager::authorize() stores jetpack_options: user_token => access_token.$user_id
4286 * - Jetpack::activate_default_modules()
4287 * - Deactivates deprecated plugins
4288 * - Activates all default modules
4289 * - Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4290 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4291 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4292 * Done!
4293 */
4294
4295 /**
4296 * Handles the page load events for the Jetpack admin page
4297 */
4298 function admin_page_load() {
4299 $error = false;
4300
4301 // Make sure we have the right body class to hook stylings for subpages off of.
4302 add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ), 20 );
4303
4304 if ( ! empty( $_GET['jetpack_restate'] ) ) {
4305 // Should only be used in intermediate redirects to preserve state across redirects
4306 self::restate();
4307 }
4308
4309 if ( isset( $_GET['connect_url_redirect'] ) ) {
4310 // @todo: Add validation against a known allowed list.
4311 $from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4312 // User clicked in the iframe to link their accounts
4313 if ( ! self::connection()->is_user_connected() ) {
4314 $redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4315
4316 add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4317 $connect_url = $this->build_connect_url( true, $redirect, $from );
4318 remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4319
4320 if ( isset( $_GET['notes_iframe'] ) ) {
4321 $connect_url .= '&notes_iframe';
4322 }
4323 wp_redirect( $connect_url );
4324 exit;
4325 } else {
4326 if ( ! isset( $_GET['calypso_env'] ) ) {
4327 self::state( 'message', 'already_authorized' );
4328 wp_safe_redirect( self::admin_url() );
4329 exit;
4330 } else {
4331 $connect_url = $this->build_connect_url( true, false, $from );
4332 $connect_url .= '&already_authorized=true';
4333 wp_redirect( $connect_url );
4334 exit;
4335 }
4336 }
4337 }
4338
4339 if ( isset( $_GET['action'] ) ) {
4340 switch ( $_GET['action'] ) {
4341 case 'authorize_redirect':
4342 self::log( 'authorize_redirect' );
4343
4344 add_filter(
4345 'allowed_redirect_hosts',
4346 function ( $domains ) {
4347 $domains[] = 'jetpack.com';
4348 $domains[] = 'jetpack.wordpress.com';
4349 $domains[] = 'wordpress.com';
4350 $domains[] = wp_parse_url( static::get_calypso_host(), PHP_URL_HOST ); // May differ from `wordpress.com`.
4351 return array_unique( $domains );
4352 }
4353 );
4354
4355 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4356 $dest_url = empty( $_GET['dest_url'] ) ? null : $_GET['dest_url'];
4357
4358 if ( ! $dest_url || ( 0 === stripos( $dest_url, 'https://jetpack.com/' ) && 0 === stripos( $dest_url, 'https://wordpress.com/' ) ) ) {
4359 // The destination URL is missing or invalid, nothing to do here.
4360 exit;
4361 }
4362
4363 if ( static::connection()->is_connected() && static::connection()->is_user_connected() ) {
4364 // The user is either already connected, or finished the connection process.
4365 wp_safe_redirect( $dest_url );
4366 exit;
4367 } elseif ( ! empty( $_GET['done'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4368 // The user decided not to proceed with setting up the connection.
4369 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4370 exit;
4371 }
4372
4373 $redirect_args = array(
4374 'page' => 'jetpack',
4375 'action' => 'authorize_redirect',
4376 'dest_url' => rawurlencode( $dest_url ),
4377 'done' => '1',
4378 );
4379
4380 if ( ! empty( $_GET['from'] ) && 'jetpack_site_only_checkout' === $_GET['from'] ) {
4381 $redirect_args['from'] = 'jetpack_site_only_checkout';
4382 }
4383
4384 wp_safe_redirect( static::build_authorize_url( self::admin_url( $redirect_args ) ) );
4385 exit;
4386 case 'authorize':
4387 _doing_it_wrong( __METHOD__, 'The `page=jetpack&action=authorize` webhook is deprecated. Use `handler=jetpack-connection-webhooks&action=authorize` instead', 'Jetpack 9.5.0' );
4388 ( new Connection_Webhooks( $this->connection_manager ) )->handle_authorize();
4389 exit;
4390 case 'register':
4391 if ( ! current_user_can( 'jetpack_connect' ) ) {
4392 $error = 'cheatin';
4393 break;
4394 }
4395 check_admin_referer( 'jetpack-register' );
4396 self::log( 'register' );
4397 self::maybe_set_version_option();
4398 $from = isset( $_GET['from'] ) ? $_GET['from'] : false;
4399 if ( $from ) {
4400 static::connection()->add_register_request_param( 'from', (string) $from );
4401 }
4402 $registered = static::connection()->try_registration();
4403 if ( is_wp_error( $registered ) ) {
4404 $error = $registered->get_error_code();
4405 self::state( 'error', $error );
4406 self::state( 'error', $registered->get_error_message() );
4407
4408 /**
4409 * Jetpack registration Error.
4410 *
4411 * @since 7.5.0
4412 *
4413 * @param string|int $error The error code.
4414 * @param \WP_Error $registered The error object.
4415 */
4416 do_action( 'jetpack_connection_register_fail', $error, $registered );
4417 break;
4418 }
4419
4420 $redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4421
4422 /**
4423 * Jetpack registration Success.
4424 *
4425 * @since 7.5.0
4426 *
4427 * @param string $from 'from' GET parameter;
4428 */
4429 do_action( 'jetpack_connection_register_success', $from );
4430
4431 $url = $this->build_connect_url( true, $redirect, $from );
4432
4433 if ( ! empty( $_GET['onboarding'] ) ) {
4434 $url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4435 }
4436
4437 if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4438 $url = add_query_arg( 'auth_approved', 'true', $url );
4439 }
4440
4441 wp_redirect( $url );
4442 exit;
4443 case 'activate':
4444 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4445 $error = 'cheatin';
4446 break;
4447 }
4448
4449 $module = stripslashes( $_GET['module'] );
4450 check_admin_referer( "jetpack_activate-$module" );
4451 self::log( 'activate', $module );
4452 if ( ! self::activate_module( $module ) ) {
4453 self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4454 }
4455 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4456 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4457 exit;
4458 case 'activate_default_modules':
4459 check_admin_referer( 'activate_default_modules' );
4460 self::log( 'activate_default_modules' );
4461 self::restate();
4462 $min_version = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4463 $max_version = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4464 $other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4465 self::activate_default_modules( $min_version, $max_version, $other_modules );
4466 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4467 exit;
4468 case 'disconnect':
4469 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4470 $error = 'cheatin';
4471 break;
4472 }
4473
4474 check_admin_referer( 'jetpack-disconnect' );
4475 self::log( 'disconnect' );
4476 self::disconnect();
4477 wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4478 exit;
4479 case 'reconnect':
4480 if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4481 $error = 'cheatin';
4482 break;
4483 }
4484
4485 check_admin_referer( 'jetpack-reconnect' );
4486 self::log( 'reconnect' );
4487 self::disconnect();
4488 wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4489 exit;
4490 case 'deactivate':
4491 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4492 $error = 'cheatin';
4493 break;
4494 }
4495
4496 $modules = stripslashes( $_GET['module'] );
4497 check_admin_referer( "jetpack_deactivate-$modules" );
4498 foreach ( explode( ',', $modules ) as $module ) {
4499 self::log( 'deactivate', $module );
4500 self::deactivate_module( $module );
4501 self::state( 'message', 'module_deactivated' );
4502 }
4503 self::state( 'module', $modules );
4504 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4505 exit;
4506 case 'unlink':
4507 $redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4508 check_admin_referer( 'jetpack-unlink' );
4509 self::log( 'unlink' );
4510 $this->connection_manager->disconnect_user();
4511 self::state( 'message', 'unlinked' );
4512 if ( 'sub-unlink' == $redirect ) {
4513 wp_safe_redirect( admin_url() );
4514 } else {
4515 wp_safe_redirect( self::admin_url( array( 'page' => $redirect ) ) );
4516 }
4517 exit;
4518 case 'onboard':
4519 if ( ! current_user_can( 'manage_options' ) ) {
4520 wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4521 } else {
4522 self::create_onboarding_token();
4523 $url = $this->build_connect_url( true );
4524
4525 if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4526 $url = add_query_arg( 'onboarding', $token, $url );
4527 }
4528
4529 $calypso_env = $this->get_calypso_env();
4530 if ( ! empty( $calypso_env ) ) {
4531 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4532 }
4533
4534 wp_redirect( $url );
4535 exit;
4536 }
4537 exit;
4538 default:
4539 /**
4540 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4541 *
4542 * @since 2.6.0
4543 *
4544 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4545 */
4546 do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4547 }
4548 }
4549
4550 if ( ! $error = $error ? $error : self::state( 'error' ) ) {
4551 self::activate_new_modules( true );
4552 }
4553
4554 $message_code = self::state( 'message' );
4555 if ( self::state( 'optin-manage' ) ) {
4556 $activated_manage = $message_code;
4557 $message_code = 'jetpack-manage';
4558 }
4559
4560 switch ( $message_code ) {
4561 case 'jetpack-manage':
4562 $sites_url = esc_url( Redirect::get_url( 'calypso-sites' ) );
4563 // translators: %s is the URL to the "Sites" panel on wordpress.com.
4564 $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>';
4565 if ( $activated_manage ) {
4566 $this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack' ) . '</strong>';
4567 }
4568 break;
4569
4570 }
4571
4572 $deactivated_plugins = self::state( 'deactivated_plugins' );
4573
4574 if ( ! empty( $deactivated_plugins ) ) {
4575 $deactivated_plugins = explode( ',', $deactivated_plugins );
4576 $deactivated_titles = array();
4577 foreach ( $deactivated_plugins as $deactivated_plugin ) {
4578 if ( ! isset( $this->plugins_to_deactivate[ $deactivated_plugin ] ) ) {
4579 continue;
4580 }
4581
4582 $deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[ $deactivated_plugin ][1] ) . '</strong>';
4583 }
4584
4585 if ( $deactivated_titles ) {
4586 if ( $this->message ) {
4587 $this->message .= "<br /><br />\n";
4588 }
4589
4590 $this->message .= wp_sprintf(
4591 _n(
4592 'Jetpack contains the most recent version of the old %l plugin.',
4593 'Jetpack contains the most recent versions of the old %l plugins.',
4594 count( $deactivated_titles ),
4595 'jetpack'
4596 ),
4597 $deactivated_titles
4598 );
4599
4600 $this->message .= "<br />\n";
4601
4602 $this->message .= _n(
4603 'The old version has been deactivated and can be removed from your site.',
4604 'The old versions have been deactivated and can be removed from your site.',
4605 count( $deactivated_titles ),
4606 'jetpack'
4607 );
4608 }
4609 }
4610
4611 $this->privacy_checks = self::state( 'privacy_checks' );
4612
4613 if ( $this->message || $this->error || $this->privacy_checks ) {
4614 add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4615 }
4616
4617 add_filter( 'jetpack_short_module_description', 'wptexturize' );
4618 }
4619
4620 function admin_notices() {
4621
4622 if ( $this->error ) {
4623 ?>
4624 <div id="message" class="jetpack-message jetpack-err">
4625 <div class="squeezer">
4626 <h2>
4627 <?php
4628 echo wp_kses(
4629 $this->error,
4630 array(
4631 'a' => array( 'href' => array() ),
4632 'small' => true,
4633 'code' => true,
4634 'strong' => true,
4635 'br' => true,
4636 'b' => true,
4637 )
4638 );
4639 ?>
4640 </h2>
4641 <?php if ( $desc = self::state( 'error_description' ) ) : ?>
4642 <p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4643 <?php endif; ?>
4644 </div>
4645 </div>
4646 <?php
4647 }
4648
4649 if ( $this->message ) {
4650 ?>
4651 <div id="message" class="jetpack-message">
4652 <div class="squeezer">
4653 <h2>
4654 <?php
4655 echo wp_kses(
4656 $this->message,
4657 array(
4658 'strong' => array(),
4659 'a' => array( 'href' => true ),
4660 'br' => true,
4661 )
4662 );
4663 ?>
4664 </h2>
4665 </div>
4666 </div>
4667 <?php
4668 }
4669
4670 if ( $this->privacy_checks ) :
4671 $module_names = $module_slugs = array();
4672
4673 $privacy_checks = explode( ',', $this->privacy_checks );
4674 $privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4675 foreach ( $privacy_checks as $module_slug ) {
4676 $module = self::get_module( $module_slug );
4677 if ( ! $module ) {
4678 continue;
4679 }
4680
4681 $module_slugs[] = $module_slug;
4682 $module_names[] = "<strong>{$module['name']}</strong>";
4683 }
4684
4685 $module_slugs = join( ',', $module_slugs );
4686 ?>
4687 <div id="message" class="jetpack-message jetpack-err">
4688 <div class="squeezer">
4689 <h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4690 <p>
4691 <?php
4692 echo wp_kses(
4693 wptexturize(
4694 wp_sprintf(
4695 _nx(
4696 "Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4697 "Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4698 count( $privacy_checks ),
4699 '%l = list of Jetpack module/feature names',
4700 'jetpack'
4701 ),
4702 $module_names
4703 )
4704 ),
4705 array( 'strong' => true )
4706 );
4707
4708 echo "\n<br />\n";
4709
4710 echo wp_kses(
4711 sprintf(
4712 _nx(
4713 'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4714 'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4715 count( $privacy_checks ),
4716 '%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4717 'jetpack'
4718 ),
4719 wp_nonce_url(
4720 self::admin_url(
4721 array(
4722 'page' => 'jetpack',
4723 'action' => 'deactivate',
4724 'module' => urlencode( $module_slugs ),
4725 )
4726 ),
4727 "jetpack_deactivate-$module_slugs"
4728 ),
4729 esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4730 ),
4731 array(
4732 'a' => array(
4733 'href' => true,
4734 'title' => true,
4735 ),
4736 )
4737 );
4738 ?>
4739 </p>
4740 </div>
4741 </div>
4742 <?php
4743 endif;
4744 }
4745
4746 /**
4747 * We can't always respond to a signed XML-RPC request with a
4748 * helpful error message. In some circumstances, doing so could
4749 * leak information.
4750 *
4751 * Instead, track that the error occurred via a Jetpack_Option,
4752 * and send that data back in the heartbeat.
4753 * All this does is increment a number, but it's enough to find
4754 * trends.
4755 *
4756 * @param WP_Error $xmlrpc_error The error produced during
4757 * signature validation.
4758 */
4759 function track_xmlrpc_error( $xmlrpc_error ) {
4760 $code = is_wp_error( $xmlrpc_error )
4761 ? $xmlrpc_error->get_error_code()
4762 : 'should-not-happen';
4763
4764 $xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4765 if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4766 // No need to update the option if we already have
4767 // this code stored.
4768 return;
4769 }
4770 $xmlrpc_errors[ $code ] = true;
4771
4772 Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
4773 }
4774
4775 /**
4776 * Initialize the jetpack stats instance only when needed
4777 *
4778 * @return void
4779 */
4780 private function initialize_stats() {
4781 if ( is_null( $this->a8c_mc_stats_instance ) ) {
4782 $this->a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4783 }
4784 }
4785
4786 /**
4787 * Record a stat for later output. This will only currently output in the admin_footer.
4788 */
4789 function stat( $group, $detail ) {
4790 $this->initialize_stats();
4791 $this->a8c_mc_stats_instance->add( $group, $detail );
4792
4793 // Keep a local copy for backward compatibility (there are some direct checks on this).
4794 $this->stats = $this->a8c_mc_stats_instance->get_current_stats();
4795 }
4796
4797 /**
4798 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4799 */
4800 function do_stats( $method = '' ) {
4801 $this->initialize_stats();
4802 if ( 'server_side' === $method ) {
4803 $this->a8c_mc_stats_instance->do_server_side_stats();
4804 } else {
4805 $this->a8c_mc_stats_instance->do_stats();
4806 }
4807
4808 // Keep a local copy for backward compatibility (there are some direct checks on this).
4809 $this->stats = array();
4810 }
4811
4812 /**
4813 * Runs stats code for a one-off, server-side.
4814 *
4815 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4816 *
4817 * @return bool If it worked.
4818 */
4819 static function do_server_side_stat( $args ) {
4820 $url = self::build_stats_url( $args );
4821 $a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4822 return $a8c_mc_stats_instance->do_server_side_stat( $url );
4823 }
4824
4825 /**
4826 * Builds the stats url.
4827 *
4828 * @param $args array|string The arguments to append to the URL.
4829 *
4830 * @return string The URL to be pinged.
4831 */
4832 static function build_stats_url( $args ) {
4833
4834 $a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4835 return $a8c_mc_stats_instance->build_stats_url( $args );
4836
4837 }
4838
4839 /**
4840 * Builds a URL to the Jetpack connection auth page
4841 *
4842 * @since 3.9.5
4843 *
4844 * @param bool $raw If true, URL will not be escaped.
4845 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4846 * If string, will be a custom redirect.
4847 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4848 * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4849 *
4850 * @return string Connect URL
4851 */
4852 function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4853 $site_id = Jetpack_Options::get_option( 'id' );
4854 $blog_token = ( new Tokens() )->get_access_token();
4855
4856 if ( $register || ! $blog_token || ! $site_id ) {
4857 $url = self::nonce_url_no_esc( self::admin_url( 'action=register' ), 'jetpack-register' );
4858
4859 if ( ! empty( $redirect ) ) {
4860 $url = add_query_arg(
4861 'redirect',
4862 urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4863 $url
4864 );
4865 }
4866
4867 if ( is_network_admin() ) {
4868 $url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4869 }
4870
4871 $calypso_env = self::get_calypso_env();
4872
4873 if ( ! empty( $calypso_env ) ) {
4874 $url = add_query_arg( 'calypso_env', $calypso_env, $url );
4875 }
4876 } else {
4877
4878 // Let's check the existing blog token to see if we need to re-register. We only check once per minute
4879 // because otherwise this logic can get us in to a loop.
4880 $last_connect_url_check = (int) Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' );
4881 if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4882 Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4883
4884 $response = Client::wpcom_json_api_request_as_blog(
4885 sprintf( '/sites/%d', $site_id ) . '?force=wpcom',
4886 '1.1'
4887 );
4888
4889 if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4890
4891 // Generating a register URL instead to refresh the existing token
4892 return $this->build_connect_url( $raw, $redirect, $from, true );
4893 }
4894 }
4895
4896 $url = $this->build_authorize_url( $redirect );
4897 }
4898
4899 if ( $from ) {
4900 $url = add_query_arg( 'from', $from, $url );
4901 }
4902
4903 $url = $raw ? esc_url_raw( $url ) : esc_url( $url );
4904 /**
4905 * Filter the URL used when connecting a user to a WordPress.com account.
4906 *
4907 * @since 8.1.0
4908 *
4909 * @param string $url Connection URL.
4910 * @param bool $raw If true, URL will not be escaped.
4911 */
4912 return apply_filters( 'jetpack_build_connection_url', $url, $raw );
4913 }
4914
4915 public static function build_authorize_url( $redirect = false, $iframe = false ) {
4916
4917 add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4918 add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4919
4920 if ( $iframe ) {
4921 add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4922 }
4923
4924 $c8n = self::connection();
4925 $url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
4926
4927 remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4928 remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4929
4930 if ( $iframe ) {
4931 remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4932 }
4933
4934 /**
4935 * Filter the URL used when authorizing a user to a WordPress.com account.
4936 *
4937 * @since 8.9.0
4938 *
4939 * @param string $url Connection URL.
4940 */
4941 return apply_filters( 'jetpack_build_authorize_url', $url );
4942 }
4943
4944 /**
4945 * Filters the connection URL parameter array.
4946 *
4947 * @param array $args default URL parameters used by the package.
4948 * @return array the modified URL arguments array.
4949 */
4950 public static function filter_connect_request_body( $args ) {
4951 if (
4952 Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4953 && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4954 ) {
4955 $gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4956 $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4957 ? $gp_locale->slug
4958 : '';
4959 }
4960
4961 $tracking = new Tracking();
4962 $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4963
4964 $args = array_merge(
4965 $args,
4966 array(
4967 '_ui' => $tracks_identity['_ui'],
4968 '_ut' => $tracks_identity['_ut'],
4969 )
4970 );
4971
4972 $calypso_env = self::get_calypso_env();
4973
4974 if ( ! empty( $calypso_env ) ) {
4975 $args['calypso_env'] = $calypso_env;
4976 }
4977
4978 return $args;
4979 }
4980
4981 /**
4982 * Filters the `jetpack/v4/connection/data` API response of the Connection package in order to
4983 * add Jetpack-the-plugin related permissions.
4984 *
4985 * @since 10.0
4986 *
4987 * @param array $current_user_connection_data An array containing the current user connection data.
4988 * @return array
4989 */
4990 public static function filter_jetpack_current_user_connection_data( $current_user_connection_data ) {
4991 $jetpack_permissions = array(
4992 'admin_page' => current_user_can( 'jetpack_admin_page' ),
4993 'manage_modules' => current_user_can( 'jetpack_manage_modules' ),
4994 'network_admin' => current_user_can( 'jetpack_network_admin_page' ),
4995 'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
4996 'edit_posts' => current_user_can( 'edit_posts' ),
4997 'publish_posts' => current_user_can( 'publish_posts' ),
4998 'manage_options' => current_user_can( 'manage_options' ),
4999 'view_stats' => current_user_can( 'view_stats' ),
5000 'manage_plugins' => current_user_can( 'install_plugins' )
5001 && current_user_can( 'activate_plugins' )
5002 && current_user_can( 'update_plugins' )
5003 && current_user_can( 'delete_plugins' ),
5004 );
5005
5006 if ( isset( $current_user_connection_data['permissions'] ) &&
5007 is_array( $current_user_connection_data['permissions'] ) ) {
5008 $current_user_connection_data['permissions'] = array_merge( $current_user_connection_data['permissions'], $jetpack_permissions );
5009 } else {
5010 $current_user_connection_data['permissions'] = $jetpack_permissions;
5011 }
5012
5013 return $current_user_connection_data;
5014 }
5015
5016 /**
5017 * Filters the URL that will process the connection data. It can be different from the URL
5018 * that we send the user to after everything is done.
5019 *
5020 * @param String $processing_url the default redirect URL used by the package.
5021 * @return String the modified URL.
5022 *
5023 * @deprecated since Jetpack 9.5.0
5024 */
5025 public static function filter_connect_processing_url( $processing_url ) {
5026 _deprecated_function( __METHOD__, 'jetpack-9.5' );
5027
5028 $processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
5029 return $processing_url;
5030 }
5031
5032 /**
5033 * Filters the redirection URL that is used for connect requests. The redirect
5034 * URL should return the user back to the Jetpack console.
5035 *
5036 * @param String $redirect the default redirect URL used by the package.
5037 * @return String the modified URL.
5038 */
5039 public static function filter_connect_redirect_url( $redirect ) {
5040 $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
5041 $redirect = $redirect
5042 ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
5043 : $jetpack_admin_page;
5044
5045 if ( isset( $_REQUEST['is_multisite'] ) ) {
5046 $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
5047 }
5048
5049 return $redirect;
5050 }
5051
5052 /**
5053 * This action fires at the beginning of the Manager::authorize method.
5054 */
5055 public static function authorize_starting() {
5056 $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
5057 // Checking if site has been active/connected previously before recording unique connection.
5058 if ( ! $jetpack_unique_connection ) {
5059 // jetpack_unique_connection option has never been set.
5060 $jetpack_unique_connection = array(
5061 'connected' => 0,
5062 'disconnected' => 0,
5063 'version' => '3.6.1',
5064 );
5065
5066 update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
5067
5068 // Track unique connection.
5069 $jetpack = self::init();
5070
5071 $jetpack->stat( 'connections', 'unique-connection' );
5072 $jetpack->do_stats( 'server_side' );
5073 }
5074
5075 // Increment number of times connected.
5076 $jetpack_unique_connection['connected'] += 1;
5077 Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
5078 }
5079
5080 /**
5081 * This action fires when the site is registered (connected at a site level).
5082 */
5083 public function handle_unique_registrations_stats() {
5084 $jetpack_unique_registrations = Jetpack_Options::get_option( 'unique_registrations' );
5085 // Checking if site has been registered previously before recording unique connection.
5086 if ( ! $jetpack_unique_registrations ) {
5087
5088 $jetpack_unique_registrations = 0;
5089
5090 $this->stat( 'connections', 'unique-registrations' );
5091 $this->do_stats( 'server_side' );
5092 }
5093
5094 // Increment number of times connected.
5095 $jetpack_unique_registrations ++;
5096 Jetpack_Options::update_option( 'unique_registrations', $jetpack_unique_registrations );
5097 }
5098
5099 /**
5100 * This action fires at the end of the Manager::authorize method when a secondary user is
5101 * linked.
5102 */
5103 public static function authorize_ending_linked() {
5104 // Don't activate anything since we are just connecting a user.
5105 self::state( 'message', 'linked' );
5106 }
5107
5108 /**
5109 * This action fires at the end of the Manager::authorize method when the master user is
5110 * authorized.
5111 *
5112 * @param array $data The request data.
5113 */
5114 public static function authorize_ending_authorized( $data ) {
5115 // If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
5116 self::invalidate_onboarding_token();
5117
5118 // If redirect_uri is SSO, ensure SSO module is enabled.
5119 parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
5120
5121 /** This filter is documented in class.jetpack-cli.php */
5122 $jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
5123
5124 $activate_sso = (
5125 isset( $redirect_options['action'] ) &&
5126 'jetpack-sso' === $redirect_options['action'] &&
5127 $jetpack_start_enable_sso
5128 );
5129
5130 $do_redirect_on_error = ( 'client' === $data['auth_type'] );
5131
5132 self::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
5133 }
5134
5135 /**
5136 * Fires on the jetpack_site_registered hook and acitvates default modules
5137 */
5138 public static function activate_default_modules_on_site_register() {
5139 $active_modules = Jetpack_Options::get_option( 'active_modules' );
5140 if ( $active_modules ) {
5141 self::delete_active_modules();
5142
5143 // 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.
5144 self::activate_default_modules( 999, 1, $active_modules, false );
5145 } else {
5146 // On a fresh new connection, at this point we activate only modules that do not require a user connection.
5147 self::activate_default_modules( false, false, array(), false, null, null, false );
5148 }
5149
5150 // Since this is a fresh connection, be sure to clear out IDC options.
5151 Identity_Crisis::clear_all_idc_options();
5152 }
5153
5154 /**
5155 * This action fires at the end of the REST_Connector connection_reconnect method when the
5156 * reconnect process is completed.
5157 * Note that this currently only happens when we don't need the user to re-authorize
5158 * their WP.com account, eg in cases where we are restoring a connection with
5159 * unhealthy blog token.
5160 */
5161 public static function reconnection_completed() {
5162 self::state( 'message', 'reconnection_completed' );
5163 }
5164
5165 /**
5166 * Get our assumed site creation date.
5167 * Calculated based on the earlier date of either:
5168 * - Earliest admin user registration date.
5169 * - Earliest date of post of any post type.
5170 *
5171 * @since 7.2.0
5172 * @deprecated since 7.8.0
5173 *
5174 * @return string Assumed site creation date and time.
5175 */
5176 public static function get_assumed_site_creation_date() {
5177 _deprecated_function( __METHOD__, 'jetpack-7.8', 'Automattic\\Jetpack\\Connection\\Manager' );
5178 return self::connection()->get_assumed_site_creation_date();
5179 }
5180
5181 public static function apply_activation_source_to_args( &$args ) {
5182 list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
5183
5184 if ( $activation_source_name ) {
5185 $args['_as'] = urlencode( $activation_source_name );
5186 }
5187
5188 if ( $activation_source_keyword ) {
5189 $args['_ak'] = urlencode( $activation_source_keyword );
5190 }
5191 }
5192
5193 function build_reconnect_url( $raw = false ) {
5194 $url = wp_nonce_url( self::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
5195 return $raw ? $url : esc_url( $url );
5196 }
5197
5198 public static function admin_url( $args = null ) {
5199 $args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
5200 $url = add_query_arg( $args, admin_url( 'admin.php' ) );
5201 return $url;
5202 }
5203
5204 public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
5205 $actionurl = str_replace( '&amp;', '&', $actionurl );
5206 return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
5207 }
5208
5209 function dismiss_jetpack_notice() {
5210
5211 if ( ! isset( $_GET['jetpack-notice'] ) ) {
5212 return;
5213 }
5214
5215 switch ( $_GET['jetpack-notice'] ) {
5216 case 'dismiss':
5217 if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
5218
5219 require_once ABSPATH . 'wp-admin/includes/plugin.php';
5220 deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
5221 wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
5222 }
5223 break;
5224 }
5225 }
5226
5227 public static function sort_modules( $a, $b ) {
5228 if ( $a['sort'] == $b['sort'] ) {
5229 return 0;
5230 }
5231
5232 return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5233 }
5234
5235 function ajax_recheck_ssl() {
5236 check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
5237 $result = self::permit_ssl( true );
5238 wp_send_json(
5239 array(
5240 'enabled' => $result,
5241 'message' => get_transient( 'jetpack_https_test_message' ),
5242 )
5243 );
5244 }
5245
5246 /* Client API */
5247
5248 /**
5249 * Returns the requested Jetpack API URL
5250 *
5251 * @deprecated since 7.7
5252 * @return string
5253 */
5254 public static function api_url( $relative_url ) {
5255 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::api_url' );
5256 $connection = self::connection();
5257 return $connection->api_url( $relative_url );
5258 }
5259
5260 /**
5261 * @deprecated 8.0
5262 *
5263 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requests.
5264 * But we no longer fix "bad hosts" anyway, outbound HTTPS is required for Jetpack to function.
5265 */
5266 public static function fix_url_for_bad_hosts( $url ) {
5267 _deprecated_function( __METHOD__, 'jetpack-8.0' );
5268 return $url;
5269 }
5270
5271 public static function verify_onboarding_token( $token_data, $token, $request_data ) {
5272 // Default to a blog token.
5273 $token_type = 'blog';
5274
5275 // Let's see if this is onboarding. In such case, use user token type and the provided user id.
5276 if ( isset( $request_data ) || ! empty( $_GET['onboarding'] ) ) {
5277 if ( ! empty( $_GET['onboarding'] ) ) {
5278 $jpo = $_GET;
5279 } else {
5280 $jpo = json_decode( $request_data, true );
5281 }
5282
5283 $jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5284 $jpo_user = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5285
5286 if (
5287 isset( $jpo_user )
5288 && isset( $jpo_token )
5289 && is_email( $jpo_user )
5290 && ctype_alnum( $jpo_token )
5291 && isset( $_GET['rest_route'] )
5292 && self::validate_onboarding_token_action(
5293 $jpo_token,
5294 $_GET['rest_route']
5295 )
5296 ) {
5297 $jp_user = get_user_by( 'email', $jpo_user );
5298 if ( is_a( $jp_user, 'WP_User' ) ) {
5299 wp_set_current_user( $jp_user->ID );
5300 $user_can = is_multisite()
5301 ? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5302 : current_user_can( 'manage_options' );
5303 if ( $user_can ) {
5304 $token_type = 'user';
5305 $token->external_user_id = $jp_user->ID;
5306 }
5307 }
5308 }
5309
5310 $token_data['type'] = $token_type;
5311 $token_data['user_id'] = $token->external_user_id;
5312 }
5313
5314 return $token_data;
5315 }
5316
5317 /**
5318 * Create a random secret for validating onboarding payload
5319 *
5320 * @return string Secret token
5321 */
5322 public static function create_onboarding_token() {
5323 if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
5324 $token = wp_generate_password( 32, false );
5325 Jetpack_Options::update_option( 'onboarding', $token );
5326 }
5327
5328 return $token;
5329 }
5330
5331 /**
5332 * Remove the onboarding token
5333 *
5334 * @return bool True on success, false on failure
5335 */
5336 public static function invalidate_onboarding_token() {
5337 return Jetpack_Options::delete_option( 'onboarding' );
5338 }
5339
5340 /**
5341 * Validate an onboarding token for a specific action
5342 *
5343 * @return boolean True if token/action pair is accepted, false if not
5344 */
5345 public static function validate_onboarding_token_action( $token, $action ) {
5346 // Compare tokens, bail if tokens do not match
5347 if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5348 return false;
5349 }
5350
5351 // List of valid actions we can take
5352 $valid_actions = array(
5353 '/jetpack/v4/settings',
5354 );
5355
5356 // Only allow valid actions.
5357 if ( ! in_array( $action, $valid_actions ) ) {
5358 return false;
5359 }
5360
5361 return true;
5362 }
5363
5364 /**
5365 * Checks to see if the URL is using SSL to connect with Jetpack
5366 *
5367 * @since 2.3.3
5368 * @return boolean
5369 */
5370 public static function permit_ssl( $force_recheck = false ) {
5371 // Do some fancy tests to see if ssl is being supported
5372 if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5373 $message = '';
5374 if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5375 $ssl = 0;
5376 } else {
5377 $ssl = 1;
5378
5379 if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5380 $ssl = 0;
5381 $message = __( 'WordPress reports no SSL support', 'jetpack' );
5382 } else {
5383 $response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5384 if ( is_wp_error( $response ) ) {
5385 $ssl = 0;
5386 $message = __( 'WordPress reports no SSL support', 'jetpack' );
5387 } elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5388 $ssl = 0;
5389 $message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5390 }
5391 }
5392 }
5393 set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5394 set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5395 }
5396
5397 return (bool) $ssl;
5398 }
5399
5400 /*
5401 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
5402 */
5403 public function alert_auto_ssl_fail() {
5404 if ( ! current_user_can( 'manage_options' ) ) {
5405 return;
5406 }
5407
5408 $ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5409 ?>
5410
5411 <div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5412 <div class="jp-banner__content">
5413 <h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5414 <p><?php _e( 'Your site could not connect to WordPress.com via HTTPS. This could be due to any number of reasons, including faulty SSL certificates, misconfigured or missing SSL libraries, or network issues.', 'jetpack' ); ?></p>
5415 <p>
5416 <?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5417 <a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5418 <span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5419 </p>
5420 <p>
5421 <?php
5422 printf(
5423 __( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
5424 esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ),
5425 esc_url( Redirect::get_url( 'jetpack-support-getting-started-troubleshooting-tips' ) )
5426 );
5427 ?>
5428 </p>
5429 </div>
5430 </div>
5431 <style>
5432 #jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5433 </style>
5434 <script type="text/javascript">
5435 jQuery( document ).ready( function( $ ) {
5436 $( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5437 var $this = $( this );
5438 $this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5439 $( '#jetpack-recheck-ssl-output' ).html( '' );
5440 e.preventDefault();
5441 var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5442 $.post( ajaxurl, data )
5443 .done( function( response ) {
5444 if ( response.enabled ) {
5445 $( '#jetpack-ssl-warning' ).hide();
5446 } else {
5447 this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5448 $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5449 }
5450 }.bind( $this ) );
5451 } );
5452 } );
5453 </script>
5454
5455 <?php
5456 }
5457
5458 /**
5459 * Returns the Jetpack XML-RPC API
5460 *
5461 * @deprecated 8.0 Use Connection_Manager instead.
5462 * @return string
5463 */
5464 public static function xmlrpc_api_url() {
5465 _deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_api_url()' );
5466 return self::connection()->xmlrpc_api_url();
5467 }
5468
5469 /**
5470 * Returns the connection manager object.
5471 *
5472 * @return Automattic\Jetpack\Connection\Manager
5473 */
5474 public static function connection() {
5475 $jetpack = static::init();
5476
5477 // If the connection manager hasn't been instantiated, do that now.
5478 if ( ! $jetpack->connection_manager ) {
5479 $jetpack->connection_manager = new Connection_Manager( 'jetpack' );
5480 }
5481
5482 return $jetpack->connection_manager;
5483 }
5484
5485 /**
5486 * Creates two secret tokens and the end of life timestamp for them.
5487 *
5488 * Note these tokens are unique per call, NOT static per site for connecting.
5489 *
5490 * @deprecated 9.5 Use Automattic\Jetpack\Connection\Secrets->generate() instead.
5491 *
5492 * @since 2.6
5493 * @param String $action The action name.
5494 * @param Integer $user_id The user identifier.
5495 * @param Integer $exp Expiration time in seconds.
5496 * @return array
5497 */
5498 public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5499 _deprecated_function( __METHOD__, 'jetpack-9.5', 'Automattic\\Jetpack\\Connection\\Secrets->generate' );
5500 return self::connection()->generate_secrets( $action, $user_id, $exp );
5501 }
5502
5503 public static function get_secrets( $action, $user_id ) {
5504 $secrets = ( new Secrets() )->get( $action, $user_id );
5505
5506 if ( Secrets::SECRETS_MISSING === $secrets ) {
5507 return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
5508 }
5509
5510 if ( Secrets::SECRETS_EXPIRED === $secrets ) {
5511 return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
5512 }
5513
5514 return $secrets;
5515 }
5516
5517 /**
5518 * Builds the timeout limit for queries talking with the wpcom servers.
5519 *
5520 * Based on local php max_execution_time in php.ini
5521 *
5522 * @since 2.6
5523 * @return int
5524 * @deprecated
5525 **/
5526 public function get_remote_query_timeout_limit() {
5527 _deprecated_function( __METHOD__, 'jetpack-5.4' );
5528 return self::get_max_execution_time();
5529 }
5530
5531 /**
5532 * Builds the timeout limit for queries talking with the wpcom servers.
5533 *
5534 * Based on local php max_execution_time in php.ini
5535 *
5536 * @since 5.4
5537 * @return int
5538 **/
5539 public static function get_max_execution_time() {
5540 $timeout = (int) ini_get( 'max_execution_time' );
5541
5542 // Ensure exec time set in php.ini
5543 if ( ! $timeout ) {
5544 $timeout = 30;
5545 }
5546 return $timeout;
5547 }
5548
5549 /**
5550 * Sets a minimum request timeout, and returns the current timeout
5551 *
5552 * @since 5.4
5553 **/
5554 public static function set_min_time_limit( $min_timeout ) {
5555 $timeout = self::get_max_execution_time();
5556 if ( $timeout < $min_timeout ) {
5557 $timeout = $min_timeout;
5558 set_time_limit( $timeout );
5559 }
5560 return $timeout;
5561 }
5562
5563 /**
5564 * Takes the response from the Jetpack register new site endpoint and
5565 * verifies it worked properly.
5566 *
5567 * @since 2.6
5568 * @deprecated since 7.7.0
5569 * @see Automattic\Jetpack\Connection\Manager::validate_remote_register_response()
5570 **/
5571 public function validate_remote_register_response() {
5572 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::validate_remote_register_response' );
5573 }
5574
5575 /**
5576 * @deprecated since Jetpack 9.7.0
5577 * @see Automattic\Jetpack\Connection\Manager::try_registration()
5578 *
5579 * @return bool|WP_Error
5580 */
5581 public static function register() {
5582 _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Manager::try_registration' );
5583 return static::connection()->try_registration( false );
5584 }
5585
5586 /**
5587 * Filters the registration request body to include tracking properties.
5588 *
5589 * @deprecated since Jetpack 9.7.0
5590 * @see Automattic\Jetpack\Connection\Utils::filter_register_request_body()
5591 *
5592 * @param array $properties
5593 * @return array amended properties.
5594 */
5595 public static function filter_register_request_body( $properties ) {
5596 _deprecated_function( __METHOD__, 'jetpack-9.7', 'Automattic\\Jetpack\\Connection\\Utils::filter_register_request_body' );
5597 return Connection_Utils::filter_register_request_body( $properties );
5598 }
5599
5600 /**
5601 * Filters the token request body to include tracking properties.
5602 *
5603 * @param array $properties
5604 * @return array amended properties.
5605 */
5606 public static function filter_token_request_body( $properties ) {
5607 $tracking = new Tracking();
5608 $tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5609
5610 return array_merge(
5611 $properties,
5612 array(
5613 '_ui' => $tracks_identity['_ui'],
5614 '_ut' => $tracks_identity['_ut'],
5615 )
5616 );
5617 }
5618
5619 /**
5620 * If the db version is showing something other that what we've got now, bump it to current.
5621 *
5622 * @return bool: True if the option was incorrect and updated, false if nothing happened.
5623 */
5624 public static function maybe_set_version_option() {
5625 list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5626 if ( JETPACK__VERSION != $version ) {
5627 Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5628
5629 if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5630 /** This action is documented in class.jetpack.php */
5631 do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5632 }
5633
5634 return true;
5635 }
5636 return false;
5637 }
5638
5639 /* Client Server API */
5640
5641 /**
5642 * Loads the Jetpack XML-RPC client.
5643 * No longer necessary, as the XML-RPC client will be automagically loaded.
5644 *
5645 * @deprecated since 7.7.0
5646 */
5647 public static function load_xml_rpc_client() {
5648 _deprecated_function( __METHOD__, 'jetpack-7.7' );
5649 }
5650
5651 /**
5652 * Resets the saved authentication state in between testing requests.
5653 *
5654 * @deprecated since 8.9.0
5655 * @see Automattic\Jetpack\Connection\Rest_Authentication::reset_saved_auth_state()
5656 */
5657 public function reset_saved_auth_state() {
5658 _deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::reset_saved_auth_state' );
5659 Connection_Rest_Authentication::init()->reset_saved_auth_state();
5660 }
5661
5662 /**
5663 * Verifies the signature of the current request.
5664 *
5665 * @deprecated since 7.7.0
5666 * @see Automattic\Jetpack\Connection\Manager::verify_xml_rpc_signature()
5667 *
5668 * @return false|array
5669 */
5670 public function verify_xml_rpc_signature() {
5671 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::verify_xml_rpc_signature' );
5672 return self::connection()->verify_xml_rpc_signature();
5673 }
5674
5675 /**
5676 * Verifies the signature of the current request.
5677 *
5678 * This function has side effects and should not be used. Instead,
5679 * use the memoized version `->verify_xml_rpc_signature()`.
5680 *
5681 * @deprecated since 7.7.0
5682 * @see Automattic\Jetpack\Connection\Manager::internal_verify_xml_rpc_signature()
5683 * @internal
5684 */
5685 private function internal_verify_xml_rpc_signature() {
5686 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::internal_verify_xml_rpc_signature' );
5687 }
5688
5689 /**
5690 * Authenticates XML-RPC and other requests from the Jetpack Server.
5691 *
5692 * @deprecated since 7.7.0
5693 * @see Automattic\Jetpack\Connection\Manager::authenticate_jetpack()
5694 *
5695 * @param \WP_User|mixed $user User object if authenticated.
5696 * @param string $username Username.
5697 * @param string $password Password string.
5698 * @return \WP_User|mixed Authenticated user or error.
5699 */
5700 public function authenticate_jetpack( $user, $username, $password ) {
5701 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::authenticate_jetpack' );
5702
5703 if ( ! $this->connection_manager ) {
5704 $this->connection_manager = new Connection_Manager();
5705 }
5706
5707 return $this->connection_manager->authenticate_jetpack( $user, $username, $password );
5708 }
5709
5710 /**
5711 * Authenticates requests from Jetpack server to WP REST API endpoints.
5712 * Uses the existing XMLRPC request signing implementation.
5713 *
5714 * @deprecated since 8.9.0
5715 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authenticate()
5716 */
5717 function wp_rest_authenticate( $user ) {
5718 _deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenticate' );
5719 return Connection_Rest_Authentication::init()->wp_rest_authenticate( $user );
5720 }
5721
5722 /**
5723 * Report authentication status to the WP REST API.
5724 *
5725 * @deprecated since 8.9.0
5726 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authentication_errors()
5727 *
5728 * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it, or another value if not
5729 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5730 */
5731 public function wp_rest_authentication_errors( $value ) {
5732 _deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenication_errors' );
5733 return Connection_Rest_Authentication::init()->wp_rest_authentication_errors( $value );
5734 }
5735
5736 /**
5737 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5738 * Capture it here so we can verify the signature later.
5739 *
5740 * @deprecated since 7.7.0
5741 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_methods()
5742 *
5743 * @param array $methods XMLRPC methods.
5744 * @return array XMLRPC methods, with the $HTTP_RAW_POST_DATA one.
5745 */
5746 public function xmlrpc_methods( $methods ) {
5747 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_methods' );
5748
5749 if ( ! $this->connection_manager ) {
5750 $this->connection_manager = new Connection_Manager();
5751 }
5752
5753 return $this->connection_manager->xmlrpc_methods( $methods );
5754 }
5755
5756 /**
5757 * Register additional public XMLRPC methods.
5758 *
5759 * @deprecated since 7.7.0
5760 * @see Automattic\Jetpack\Connection\Manager::public_xmlrpc_methods()
5761 *
5762 * @param array $methods Public XMLRPC methods.
5763 * @return array Public XMLRPC methods, with the getOptions one.
5764 */
5765 public function public_xmlrpc_methods( $methods ) {
5766 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::public_xmlrpc_methods' );
5767
5768 if ( ! $this->connection_manager ) {
5769 $this->connection_manager = new Connection_Manager();
5770 }
5771
5772 return $this->connection_manager->public_xmlrpc_methods( $methods );
5773 }
5774
5775 /**
5776 * Handles a getOptions XMLRPC method call.
5777 *
5778 * @deprecated since 7.7.0
5779 * @see Automattic\Jetpack\Connection\Manager::jetpack_getOptions()
5780 *
5781 * @param array $args method call arguments.
5782 * @return array an amended XMLRPC server options array.
5783 */
5784 public function jetpack_getOptions( $args ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
5785 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::jetpack_getOptions' );
5786
5787 if ( ! $this->connection_manager ) {
5788 $this->connection_manager = new Connection_Manager();
5789 }
5790
5791 return $this->connection_manager->jetpack_getOptions( $args );
5792 }
5793
5794 /**
5795 * Adds Jetpack-specific options to the output of the XMLRPC options method.
5796 *
5797 * @deprecated since 7.7.0
5798 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_options()
5799 *
5800 * @param array $options Standard Core options.
5801 * @return array Amended options.
5802 */
5803 public function xmlrpc_options( $options ) {
5804 _deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_options' );
5805
5806 if ( ! $this->connection_manager ) {
5807 $this->connection_manager = new Connection_Manager();
5808 }
5809
5810 return $this->connection_manager->xmlrpc_options( $options );
5811 }
5812
5813 /**
5814 * State is passed via cookies from one request to the next, but never to subsequent requests.
5815 * SET: state( $key, $value );
5816 * GET: $value = state( $key );
5817 *
5818 * @param string $key
5819 * @param string $value
5820 * @param bool $restate private
5821 */
5822 public static function state( $key = null, $value = null, $restate = false ) {
5823 static $state = array();
5824 static $path, $domain;
5825 if ( ! isset( $path ) ) {
5826 require_once ABSPATH . 'wp-admin/includes/plugin.php';
5827 $admin_url = self::admin_url();
5828 $bits = wp_parse_url( $admin_url );
5829
5830 if ( is_array( $bits ) ) {
5831 $path = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5832 $domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5833 } else {
5834 $path = $domain = null;
5835 }
5836 }
5837
5838 // Extract state from cookies and delete cookies
5839 if ( isset( $_COOKIE['jetpackState'] ) && is_array( $_COOKIE['jetpackState'] ) ) {
5840 $yum = wp_unslash( $_COOKIE['jetpackState'] );
5841 unset( $_COOKIE['jetpackState'] );
5842 foreach ( $yum as $k => $v ) {
5843 if ( strlen( $v ) ) {
5844 $state[ $k ] = $v;
5845 }
5846 setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5847 }
5848 }
5849
5850 if ( $restate ) {
5851 foreach ( $state as $k => $v ) {
5852 setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5853 }
5854 return;
5855 }
5856
5857 // Get a state variable.
5858 if ( isset( $key ) && ! isset( $value ) ) {
5859 if ( array_key_exists( $key, $state ) ) {
5860 return $state[ $key ];
5861 }
5862 return null;
5863 }
5864
5865 // Set a state variable.
5866 if ( isset( $key ) && isset( $value ) ) {
5867 if ( is_array( $value ) && isset( $value[0] ) ) {
5868 $value = $value[0];
5869 }
5870 $state[ $key ] = $value;
5871 if ( ! headers_sent() ) {
5872 if ( self::should_set_cookie( $key ) ) {
5873 setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5874 }
5875 }
5876 }
5877 }
5878
5879 public static function restate() {
5880 self::state( null, null, true );
5881 }
5882
5883 /**
5884 * Determines whether the jetpackState[$key] value should be added to the
5885 * cookie.
5886 *
5887 * @param string $key The state key.
5888 *
5889 * @return boolean Whether the value should be added to the cookie.
5890 */
5891 public static function should_set_cookie( $key ) {
5892 global $current_screen;
5893 $page = isset( $current_screen->base ) ? $current_screen->base : null;
5894
5895 if ( 'toplevel_page_jetpack' === $page && 'display_update_modal' === $key ) {
5896 return false;
5897 }
5898
5899 return true;
5900 }
5901
5902 public static function check_privacy( $file ) {
5903 static $is_site_publicly_accessible = null;
5904
5905 if ( is_null( $is_site_publicly_accessible ) ) {
5906 $is_site_publicly_accessible = false;
5907
5908 $rpc = new Jetpack_IXR_Client();
5909
5910 $success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5911 if ( $success ) {
5912 $response = $rpc->getResponse();
5913 if ( $response ) {
5914 $is_site_publicly_accessible = true;
5915 }
5916 }
5917
5918 Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5919 }
5920
5921 if ( $is_site_publicly_accessible ) {
5922 return;
5923 }
5924
5925 $module_slug = self::get_module_slug( $file );
5926
5927 $privacy_checks = self::state( 'privacy_checks' );
5928 if ( ! $privacy_checks ) {
5929 $privacy_checks = $module_slug;
5930 } else {
5931 $privacy_checks .= ",$module_slug";
5932 }
5933
5934 self::state( 'privacy_checks', $privacy_checks );
5935 }
5936
5937 /**
5938 * Helper method for multicall XMLRPC.
5939 *
5940 * @deprecated since 8.9.0
5941 * @see Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call()
5942 *
5943 * @param ...$args Args for the async_call.
5944 */
5945 public static function xmlrpc_async_call( ...$args ) {
5946
5947 _deprecated_function( 'Jetpack::xmlrpc_async_call', 'jetpack-8.9.0', 'Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call' );
5948
5949 global $blog_id;
5950 static $clients = array();
5951
5952 $client_blog_id = is_multisite() ? $blog_id : 0;
5953
5954 if ( ! isset( $clients[ $client_blog_id ] ) ) {
5955 $clients[ $client_blog_id ] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => true ) );
5956 if ( function_exists( 'ignore_user_abort' ) ) {
5957 ignore_user_abort( true );
5958 }
5959 add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5960 }
5961
5962 if ( ! empty( $args[0] ) ) {
5963 call_user_func_array( array( $clients[ $client_blog_id ], 'addCall' ), $args );
5964 } elseif ( is_multisite() ) {
5965 foreach ( $clients as $client_blog_id => $client ) {
5966 if ( ! $client_blog_id || empty( $client->calls ) ) {
5967 continue;
5968 }
5969
5970 $switch_success = switch_to_blog( $client_blog_id, true );
5971 if ( ! $switch_success ) {
5972 continue;
5973 }
5974
5975 flush();
5976 $client->query();
5977
5978 restore_current_blog();
5979 }
5980 } else {
5981 if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5982 flush();
5983 $clients[0]->query();
5984 }
5985 }
5986 }
5987
5988 /**
5989 * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5990 *
5991 * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5992 *
5993 * @param string $url WordPress.com static resource URL.
5994 */
5995 public static function staticize_subdomain( $url ) {
5996 _deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5997 return Assets::staticize_subdomain( $url );
5998 }
5999
6000 /* JSON API Authorization */
6001
6002 /**
6003 * Handles the login action for Authorizing the JSON API
6004 */
6005 function login_form_json_api_authorization() {
6006 $this->verify_json_api_authorization_request();
6007
6008 add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
6009
6010 add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
6011 add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
6012 add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
6013 }
6014
6015 // Make sure the login form is POSTed to the signed URL so we can reverify the request
6016 function post_login_form_to_signed_url( $url, $path, $scheme ) {
6017 if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
6018 return $url;
6019 }
6020
6021 $parsed_url = wp_parse_url( $url );
6022 $url = strtok( $url, '?' );
6023 $url = "$url?{$_SERVER['QUERY_STRING']}";
6024 if ( ! empty( $parsed_url['query'] ) ) {
6025 $url .= "&{$parsed_url['query']}";
6026 }
6027
6028 return $url;
6029 }
6030
6031 // Make sure the POSTed request is handled by the same action
6032 function preserve_action_in_login_form_for_json_api_authorization() {
6033 echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
6034 echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
6035 }
6036
6037 // If someone logs in to approve API access, store the Access Code in usermeta
6038 function store_json_api_authorization_token( $user_login, $user ) {
6039 add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
6040 add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
6041 $token = wp_generate_password( 32, false );
6042 update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
6043 }
6044
6045 // Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
6046 function allow_wpcom_public_api_domain( $domains ) {
6047 $domains[] = 'public-api.wordpress.com';
6048 return $domains;
6049 }
6050
6051 static function is_redirect_encoded( $redirect_url ) {
6052 return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
6053 }
6054
6055 // Add all wordpress.com environments to the safe redirect allowed list.
6056 function allow_wpcom_environments( $domains ) {
6057 $domains[] = 'wordpress.com';
6058 $domains[] = 'wpcalypso.wordpress.com';
6059 $domains[] = 'horizon.wordpress.com';
6060 $domains[] = 'calypso.localhost';
6061 return $domains;
6062 }
6063
6064 // Add the Access Code details to the public-api.wordpress.com redirect
6065 function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
6066 return add_query_arg(
6067 urlencode_deep(
6068 array(
6069 'jetpack-code' => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
6070 'jetpack-user-id' => (int) $user->ID,
6071 'jetpack-state' => $this->json_api_authorization_request['state'],
6072 )
6073 ),
6074 $redirect_to
6075 );
6076 }
6077
6078 /**
6079 * Verifies the request by checking the signature
6080 *
6081 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
6082 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
6083 *
6084 * @param null|array $environment
6085 */
6086 function verify_json_api_authorization_request( $environment = null ) {
6087 $environment = is_null( $environment )
6088 ? $_REQUEST
6089 : $environment;
6090
6091 list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
6092 $token = ( new Tokens() )->get_access_token( $env_user_id, $env_token );
6093 if ( ! $token || empty( $token->secret ) ) {
6094 wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
6095 }
6096
6097 $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' );
6098
6099 // Host has encoded the request URL, probably as a result of a bad http => https redirect
6100 if ( self::is_redirect_encoded( $_GET['redirect_to'] ) ) {
6101 /**
6102 * Jetpack authorisation request Error.
6103 *
6104 * @since 7.5.0
6105 */
6106 do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
6107 $die_error = sprintf(
6108 /* translators: %s is a URL */
6109 __( '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' ),
6110 Redirect::get_url( 'jetpack-support-double-encoding' )
6111 );
6112 }
6113
6114 $jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
6115
6116 if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
6117 $signature = $jetpack_signature->sign_request(
6118 $environment['token'],
6119 $environment['timestamp'],
6120 $environment['nonce'],
6121 '',
6122 'GET',
6123 $environment['jetpack_json_api_original_query'],
6124 null,
6125 true
6126 );
6127 } else {
6128 $signature = $jetpack_signature->sign_current_request(
6129 array(
6130 'body' => null,
6131 'method' => 'GET',
6132 )
6133 );
6134 }
6135
6136 if ( ! $signature ) {
6137 wp_die( $die_error );
6138 } elseif ( is_wp_error( $signature ) ) {
6139 wp_die( $die_error );
6140 } elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
6141 if ( is_ssl() ) {
6142 // 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
6143 $signature = $jetpack_signature->sign_current_request(
6144 array(
6145 'scheme' => 'http',
6146 'body' => null,
6147 'method' => 'GET',
6148 )
6149 );
6150 if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
6151 wp_die( $die_error );
6152 }
6153 } else {
6154 wp_die( $die_error );
6155 }
6156 }
6157
6158 $timestamp = (int) $environment['timestamp'];
6159 $nonce = stripslashes( (string) $environment['nonce'] );
6160
6161 if ( ! $this->connection_manager ) {
6162 $this->connection_manager = new Connection_Manager();
6163 }
6164
6165 if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
6166 // De-nonce the nonce, at least for 5 minutes.
6167 // 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)
6168 $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
6169 if ( $old_nonce_time < time() - 300 ) {
6170 wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack' ) );
6171 }
6172 }
6173
6174 $data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
6175 $data_filters = array(
6176 'state' => 'opaque',
6177 'client_id' => 'int',
6178 'client_title' => 'string',
6179 'client_image' => 'url',
6180 );
6181
6182 foreach ( $data_filters as $key => $sanitation ) {
6183 if ( ! isset( $data->$key ) ) {
6184 wp_die( $die_error );
6185 }
6186
6187 switch ( $sanitation ) {
6188 case 'int':
6189 $this->json_api_authorization_request[ $key ] = (int) $data->$key;
6190 break;
6191 case 'opaque':
6192 $this->json_api_authorization_request[ $key ] = (string) $data->$key;
6193 break;
6194 case 'string':
6195 $this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
6196 break;
6197 case 'url':
6198 $this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
6199 break;
6200 }
6201 }
6202
6203 if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
6204 wp_die( $die_error );
6205 }
6206 }
6207
6208 function login_message_json_api_authorization( $message ) {
6209 return '<p class="message">' . sprintf(
6210 /* translators: Name/image of the client requesting authorization */
6211 esc_html__( '%s wants to access your site’s data. Log in to authorize that access.', 'jetpack' ),
6212 '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
6213 ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
6214 }
6215
6216 /**
6217 * Get $content_width, but with a <s>twist</s> filter.
6218 */
6219 public static function get_content_width() {
6220 $content_width = ( isset( $GLOBALS['content_width'] ) && is_numeric( $GLOBALS['content_width'] ) )
6221 ? $GLOBALS['content_width']
6222 : false;
6223 /**
6224 * Filter the Content Width value.
6225 *
6226 * @since 2.2.3
6227 *
6228 * @param string $content_width Content Width value.
6229 */
6230 return apply_filters( 'jetpack_content_width', $content_width );
6231 }
6232
6233 /**
6234 * Pings the WordPress.com Mirror Site for the specified options.
6235 *
6236 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6237 *
6238 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6239 */
6240 public function get_cloud_site_options( $option_names ) {
6241 $option_names = array_filter( (array) $option_names, 'is_string' );
6242
6243 $xml = new Jetpack_IXR_Client();
6244 $xml->query( 'jetpack.fetchSiteOptions', $option_names );
6245 if ( $xml->isError() ) {
6246 return array(
6247 'error_code' => $xml->getErrorCode(),
6248 'error_msg' => $xml->getErrorMessage(),
6249 );
6250 }
6251 $cloud_site_options = $xml->getResponse();
6252
6253 return $cloud_site_options;
6254 }
6255
6256 /**
6257 * Checks if the site is currently in an identity crisis.
6258 *
6259 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6260 */
6261 public static function check_identity_crisis() {
6262 if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() || ! Identity_Crisis::validate_sync_error_idc_option() ) {
6263 return false;
6264 }
6265
6266 return Jetpack_Options::get_option( 'sync_error_idc' );
6267 }
6268
6269 /**
6270 * Checks whether the home and siteurl specifically are allowed.
6271 * Written so that we don't have re-check $key and $value params every time
6272 * we want to check if this site is allowed, for example in footer.php
6273 *
6274 * @since 3.8.0
6275 * @return bool True = already allowed False = not on the allowed list.
6276 */
6277 public static function is_staging_site() {
6278 _deprecated_function( 'Jetpack::is_staging_site', 'jetpack-8.1', '/Automattic/Jetpack/Status->is_staging_site' );
6279 return ( new Status() )->is_staging_site();
6280 }
6281
6282 /**
6283 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6284 *
6285 * @since 4.4.0
6286 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6287 *
6288 * @return bool
6289 */
6290 public static function validate_sync_error_idc_option() {
6291 _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::validate_sync_error_idc_option' );
6292 return Identity_Crisis::validate_sync_error_idc_option();
6293 }
6294
6295 /**
6296 * Normalizes a url by doing three things:
6297 * - Strips protocol
6298 * - Strips www
6299 * - Adds a trailing slash
6300 *
6301 * @since 4.4.0
6302 * @param string $url
6303 * @return WP_Error|string
6304 */
6305 public static function normalize_url_protocol_agnostic( $url ) {
6306 $parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6307 if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
6308 return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) );
6309 }
6310
6311 // Strip www and protocols
6312 $url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6313 return $url;
6314 }
6315
6316 /**
6317 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6318 *
6319 * @since 4.4.0
6320 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6321 *
6322 * @param array $response
6323 * @return array Array of the local urls, wpcom urls, and error code
6324 */
6325 public static function get_sync_error_idc_option( $response = array() ) {
6326 _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::get_sync_error_idc_option' );
6327 return Identity_Crisis::get_sync_error_idc_option( $response );
6328 }
6329
6330 /**
6331 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6332 * If set to true, the site will be put into staging mode.
6333 *
6334 * @since 4.3.2
6335 * @return bool
6336 */
6337 public static function sync_idc_optin() {
6338 _deprecated_function( __METHOD__, 'jetpack-9.8', '\\Automattic\\Jetpack\\Identity_Crisis::sync_idc_optin' );
6339 return Identity_Crisis::sync_idc_optin();
6340 }
6341
6342 /**
6343 * Maybe Use a .min.css stylesheet, maybe not.
6344 *
6345 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6346 */
6347 public static function maybe_min_asset( $url, $path, $plugin ) {
6348 // Short out on things trying to find actual paths.
6349 if ( ! $path || empty( $plugin ) ) {
6350 return $url;
6351 }
6352
6353 $path = ltrim( $path, '/' );
6354
6355 // Strip out the abspath.
6356 $base = dirname( plugin_basename( $plugin ) );
6357
6358 // Short out on non-Jetpack assets.
6359 if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6360 return $url;
6361 }
6362
6363 // File name parsing.
6364 $file = "{$base}/{$path}";
6365 $full_path = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6366 $file_name = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6367 $file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6368 $extension = array_shift( $file_name_parts_r );
6369
6370 if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6371 // Already pointing at the minified version.
6372 if ( 'min' === $file_name_parts_r[0] ) {
6373 return $url;
6374 }
6375
6376 $min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6377 if ( file_exists( $min_full_path ) ) {
6378 $url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6379 // If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6380 if ( 'css' === $extension ) {
6381 $key = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6382 self::$min_assets[ $key ] = $path;
6383 }
6384 }
6385 }
6386
6387 return $url;
6388 }
6389
6390 /**
6391 * If the asset is minified, let's flag .min as the suffix.
6392 *
6393 * Attached to `style_loader_src` filter.
6394 *
6395 * @param string $tag The tag that would link to the external asset.
6396 * @param string $handle The registered handle of the script in question.
6397 * @param string $href The url of the asset in question.
6398 */
6399 public static function set_suffix_on_min( $src, $handle ) {
6400 if ( false === strpos( $src, '.min.css' ) ) {
6401 return $src;
6402 }
6403
6404 if ( ! empty( self::$min_assets ) ) {
6405 foreach ( self::$min_assets as $file => $path ) {
6406 if ( false !== strpos( $src, $file ) ) {
6407 wp_style_add_data( $handle, 'suffix', '.min' );
6408 return $src;
6409 }
6410 }
6411 }
6412
6413 return $src;
6414 }
6415
6416 /**
6417 * Maybe inlines a stylesheet.
6418 *
6419 * If you'd like to inline a stylesheet instead of printing a link to it,
6420 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6421 *
6422 * Attached to `style_loader_tag` filter.
6423 *
6424 * @param string $tag The tag that would link to the external asset.
6425 * @param string $handle The registered handle of the script in question.
6426 *
6427 * @return string
6428 */
6429 public static function maybe_inline_style( $tag, $handle ) {
6430 global $wp_styles;
6431 $item = $wp_styles->registered[ $handle ];
6432
6433 if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6434 return $tag;
6435 }
6436
6437 if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6438 $href = $matches[1];
6439 // Strip off query string
6440 if ( $pos = strpos( $href, '?' ) ) {
6441 $href = substr( $href, 0, $pos );
6442 }
6443 // Strip off fragment
6444 if ( $pos = strpos( $href, '#' ) ) {
6445 $href = substr( $href, 0, $pos );
6446 }
6447 } else {
6448 return $tag;
6449 }
6450
6451 $plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6452 if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6453 return $tag;
6454 }
6455
6456 // If this stylesheet has a RTL version, and the RTL version replaces normal...
6457 if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6458 // And this isn't the pass that actually deals with the RTL version...
6459 if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6460 // Short out, as the RTL version will deal with it in a moment.
6461 return $tag;
6462 }
6463 }
6464
6465 $file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6466 $css = self::absolutize_css_urls( file_get_contents( $file ), $href );
6467 if ( $css ) {
6468 $tag = "<!-- Inline {$item->handle} -->\r\n";
6469 if ( empty( $item->extra['after'] ) ) {
6470 wp_add_inline_style( $handle, $css );
6471 } else {
6472 array_unshift( $item->extra['after'], $css );
6473 wp_style_add_data( $handle, 'after', $item->extra['after'] );
6474 }
6475 }
6476
6477 return $tag;
6478 }
6479
6480 /**
6481 * Loads a view file from the views
6482 *
6483 * Data passed in with the $data parameter will be available in the
6484 * template file as $data['value']
6485 *
6486 * @param string $template - Template file to load
6487 * @param array $data - Any data to pass along to the template
6488 * @return boolean - If template file was found
6489 **/
6490 public function load_view( $template, $data = array() ) {
6491 $views_dir = JETPACK__PLUGIN_DIR . 'views/';
6492
6493 if ( file_exists( $views_dir . $template ) ) {
6494 require_once $views_dir . $template;
6495 return true;
6496 }
6497
6498 error_log( "Jetpack: Unable to find view file $views_dir$template" );
6499 return false;
6500 }
6501
6502 /**
6503 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
6504 */
6505 public function deprecated_hooks() {
6506 $filter_deprecated_list = array(
6507 'jetpack_bail_on_shortcode' => array(
6508 'replacement' => 'jetpack_shortcodes_to_include',
6509 'version' => 'jetpack-3.1.0',
6510 ),
6511 'wpl_sharing_2014_1' => array(
6512 'replacement' => null,
6513 'version' => 'jetpack-3.6.0',
6514 ),
6515 'jetpack-tools-to-include' => array(
6516 'replacement' => 'jetpack_tools_to_include',
6517 'version' => 'jetpack-3.9.0',
6518 ),
6519 'jetpack_identity_crisis_options_to_check' => array(
6520 'replacement' => null,
6521 'version' => 'jetpack-4.0.0',
6522 ),
6523 'update_option_jetpack_single_user_site' => array(
6524 'replacement' => null,
6525 'version' => 'jetpack-4.3.0',
6526 ),
6527 'audio_player_default_colors' => array(
6528 'replacement' => null,
6529 'version' => 'jetpack-4.3.0',
6530 ),
6531 'add_option_jetpack_featured_images_enabled' => array(
6532 'replacement' => null,
6533 'version' => 'jetpack-4.3.0',
6534 ),
6535 'add_option_jetpack_update_details' => array(
6536 'replacement' => null,
6537 'version' => 'jetpack-4.3.0',
6538 ),
6539 'add_option_jetpack_updates' => array(
6540 'replacement' => null,
6541 'version' => 'jetpack-4.3.0',
6542 ),
6543 'add_option_jetpack_network_name' => array(
6544 'replacement' => null,
6545 'version' => 'jetpack-4.3.0',
6546 ),
6547 'add_option_jetpack_network_allow_new_registrations' => array(
6548 'replacement' => null,
6549 'version' => 'jetpack-4.3.0',
6550 ),
6551 'add_option_jetpack_network_add_new_users' => array(
6552 'replacement' => null,
6553 'version' => 'jetpack-4.3.0',
6554 ),
6555 'add_option_jetpack_network_site_upload_space' => array(
6556 'replacement' => null,
6557 'version' => 'jetpack-4.3.0',
6558 ),
6559 'add_option_jetpack_network_upload_file_types' => array(
6560 'replacement' => null,
6561 'version' => 'jetpack-4.3.0',
6562 ),
6563 'add_option_jetpack_network_enable_administration_menus' => array(
6564 'replacement' => null,
6565 'version' => 'jetpack-4.3.0',
6566 ),
6567 'add_option_jetpack_is_multi_site' => array(
6568 'replacement' => null,
6569 'version' => 'jetpack-4.3.0',
6570 ),
6571 'add_option_jetpack_is_main_network' => array(
6572 'replacement' => null,
6573 'version' => 'jetpack-4.3.0',
6574 ),
6575 'add_option_jetpack_main_network_site' => array(
6576 'replacement' => null,
6577 'version' => 'jetpack-4.3.0',
6578 ),
6579 'jetpack_sync_all_registered_options' => array(
6580 'replacement' => null,
6581 'version' => 'jetpack-4.3.0',
6582 ),
6583 'jetpack_has_identity_crisis' => array(
6584 'replacement' => 'jetpack_sync_error_idc_validation',
6585 'version' => 'jetpack-4.4.0',
6586 ),
6587 'jetpack_is_post_mailable' => array(
6588 'replacement' => null,
6589 'version' => 'jetpack-4.4.0',
6590 ),
6591 'jetpack_seo_site_host' => array(
6592 'replacement' => null,
6593 'version' => 'jetpack-5.1.0',
6594 ),
6595 'jetpack_installed_plugin' => array(
6596 'replacement' => 'jetpack_plugin_installed',
6597 'version' => 'jetpack-6.0.0',
6598 ),
6599 'jetpack_holiday_snow_option_name' => array(
6600 'replacement' => null,
6601 'version' => 'jetpack-6.0.0',
6602 ),
6603 'jetpack_holiday_chance_of_snow' => array(
6604 'replacement' => null,
6605 'version' => 'jetpack-6.0.0',
6606 ),
6607 'jetpack_holiday_snow_js_url' => array(
6608 'replacement' => null,
6609 'version' => 'jetpack-6.0.0',
6610 ),
6611 'jetpack_is_holiday_snow_season' => array(
6612 'replacement' => null,
6613 'version' => 'jetpack-6.0.0',
6614 ),
6615 'jetpack_holiday_snow_option_updated' => array(
6616 'replacement' => null,
6617 'version' => 'jetpack-6.0.0',
6618 ),
6619 'jetpack_holiday_snowing' => array(
6620 'replacement' => null,
6621 'version' => 'jetpack-6.0.0',
6622 ),
6623 'jetpack_sso_auth_cookie_expirtation' => array(
6624 'replacement' => 'jetpack_sso_auth_cookie_expiration',
6625 'version' => 'jetpack-6.1.0',
6626 ),
6627 'jetpack_cache_plans' => array(
6628 'replacement' => null,
6629 'version' => 'jetpack-6.1.0',
6630 ),
6631
6632 'jetpack_lazy_images_skip_image_with_atttributes' => array(
6633 'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
6634 'version' => 'jetpack-6.5.0',
6635 ),
6636 'jetpack_enable_site_verification' => array(
6637 'replacement' => null,
6638 'version' => 'jetpack-6.5.0',
6639 ),
6640 'can_display_jetpack_manage_notice' => array(
6641 'replacement' => null,
6642 'version' => 'jetpack-7.3.0',
6643 ),
6644 'atd_http_post_timeout' => array(
6645 'replacement' => null,
6646 'version' => 'jetpack-7.3.0',
6647 ),
6648 'atd_service_domain' => array(
6649 'replacement' => null,
6650 'version' => 'jetpack-7.3.0',
6651 ),
6652 'atd_load_scripts' => array(
6653 'replacement' => null,
6654 'version' => 'jetpack-7.3.0',
6655 ),
6656 'jetpack_widget_authors_exclude' => array(
6657 'replacement' => 'jetpack_widget_authors_params',
6658 'version' => 'jetpack-7.7.0',
6659 ),
6660 // Removed in Jetpack 7.9.0
6661 'jetpack_pwa_manifest' => array(
6662 'replacement' => null,
6663 'version' => 'jetpack-7.9.0',
6664 ),
6665 'jetpack_pwa_background_color' => array(
6666 'replacement' => null,
6667 'version' => 'jetpack-7.9.0',
6668 ),
6669 'jetpack_check_mobile' => array(
6670 'replacement' => null,
6671 'version' => 'jetpack-8.3.0',
6672 ),
6673 'jetpack_mobile_stylesheet' => array(
6674 'replacement' => null,
6675 'version' => 'jetpack-8.3.0',
6676 ),
6677 'jetpack_mobile_template' => array(
6678 'replacement' => null,
6679 'version' => 'jetpack-8.3.0',
6680 ),
6681 'jetpack_mobile_theme_menu' => array(
6682 'replacement' => null,
6683 'version' => 'jetpack-8.3.0',
6684 ),
6685 'minileven_show_featured_images' => array(
6686 'replacement' => null,
6687 'version' => 'jetpack-8.3.0',
6688 ),
6689 'minileven_attachment_size' => array(
6690 'replacement' => null,
6691 'version' => 'jetpack-8.3.0',
6692 ),
6693 'instagram_cache_oembed_api_response_body' => array(
6694 'replacement' => null,
6695 'version' => 'jetpack-9.1.0',
6696 ),
6697 'jetpack_can_make_outbound_https' => array(
6698 'replacement' => null,
6699 'version' => 'jetpack-9.1.0',
6700 ),
6701 );
6702
6703 foreach ( $filter_deprecated_list as $tag => $args ) {
6704 if ( has_filter( $tag ) ) {
6705 apply_filters_deprecated( $tag, array( null ), $args['version'], $args['replacement'] );
6706 }
6707 }
6708
6709 $action_deprecated_list = array(
6710 'jetpack_updated_theme' => array(
6711 'replacement' => 'jetpack_updated_themes',
6712 'version' => 'jetpack-6.2.0',
6713 ),
6714 'atd_http_post_error' => array(
6715 'replacement' => null,
6716 'version' => 'jetpack-7.3.0',
6717 ),
6718 'mobile_reject_mobile' => array(
6719 'replacement' => null,
6720 'version' => 'jetpack-8.3.0',
6721 ),
6722 'mobile_force_mobile' => array(
6723 'replacement' => null,
6724 'version' => 'jetpack-8.3.0',
6725 ),
6726 'mobile_app_promo_download' => array(
6727 'replacement' => null,
6728 'version' => 'jetpack-8.3.0',
6729 ),
6730 'mobile_setup' => array(
6731 'replacement' => null,
6732 'version' => 'jetpack-8.3.0',
6733 ),
6734 'jetpack_mobile_footer_before' => array(
6735 'replacement' => null,
6736 'version' => 'jetpack-8.3.0',
6737 ),
6738 'wp_mobile_theme_footer' => array(
6739 'replacement' => null,
6740 'version' => 'jetpack-8.3.0',
6741 ),
6742 'minileven_credits' => array(
6743 'replacement' => null,
6744 'version' => 'jetpack-8.3.0',
6745 ),
6746 'jetpack_mobile_header_before' => array(
6747 'replacement' => null,
6748 'version' => 'jetpack-8.3.0',
6749 ),
6750 'jetpack_mobile_header_after' => array(
6751 'replacement' => null,
6752 'version' => 'jetpack-8.3.0',
6753 ),
6754 );
6755
6756 foreach ( $action_deprecated_list as $tag => $args ) {
6757 if ( has_action( $tag ) ) {
6758 do_action_deprecated( $tag, array(), $args['version'], $args['replacement'] );
6759 }
6760 }
6761 }
6762
6763 /**
6764 * Converts any url in a stylesheet, to the correct absolute url.
6765 *
6766 * Considerations:
6767 * - Normal, relative URLs `feh.png`
6768 * - Data URLs `data:image/gif;base64,eh129ehiuehjdhsa==`
6769 * - Schema-agnostic URLs `//domain.com/feh.png`
6770 * - Absolute URLs `http://domain.com/feh.png`
6771 * - Domain root relative URLs `/feh.png`
6772 *
6773 * @param $css string: The raw CSS -- should be read in directly from the file.
6774 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6775 *
6776 * @return mixed|string
6777 */
6778 public static function absolutize_css_urls( $css, $css_file_url ) {
6779 $pattern = '#url\((?P<path>[^)]*)\)#i';
6780 $css_dir = dirname( $css_file_url );
6781 $p = wp_parse_url( $css_dir );
6782 $domain = sprintf(
6783 '%1$s//%2$s%3$s%4$s',
6784 isset( $p['scheme'] ) ? "{$p['scheme']}:" : '',
6785 isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6786 $p['host'],
6787 isset( $p['port'] ) ? ":{$p['port']}" : ''
6788 );
6789
6790 if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6791 $find = $replace = array();
6792 foreach ( $matches as $match ) {
6793 $url = trim( $match['path'], "'\" \t" );
6794
6795 // If this is a data url, we don't want to mess with it.
6796 if ( 'data:' === substr( $url, 0, 5 ) ) {
6797 continue;
6798 }
6799
6800 // If this is an absolute or protocol-agnostic url,
6801 // we don't want to mess with it.
6802 if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6803 continue;
6804 }
6805
6806 switch ( substr( $url, 0, 1 ) ) {
6807 case '/':
6808 $absolute = $domain . $url;
6809 break;
6810 default:
6811 $absolute = $css_dir . '/' . $url;
6812 }
6813
6814 $find[] = $match[0];
6815 $replace[] = sprintf( 'url("%s")', $absolute );
6816 }
6817 $css = str_replace( $find, $replace, $css );
6818 }
6819
6820 return $css;
6821 }
6822
6823 /**
6824 * This methods removes all of the registered css files on the front end
6825 * from Jetpack in favor of using a single file. In effect "imploding"
6826 * all the files into one file.
6827 *
6828 * Pros:
6829 * - Uses only ONE css asset connection instead of 15
6830 * - Saves a minimum of 56k
6831 * - Reduces server load
6832 * - Reduces time to first painted byte
6833 *
6834 * Cons:
6835 * - Loads css for ALL modules. However all selectors are prefixed so it
6836 * should not cause any issues with themes.
6837 * - Plugins/themes dequeuing styles no longer do anything. See
6838 * jetpack_implode_frontend_css filter for a workaround
6839 *
6840 * For some situations developers may wish to disable css imploding and
6841 * instead operate in legacy mode where each file loads seperately and
6842 * can be edited individually or dequeued. This can be accomplished with
6843 * the following line:
6844 *
6845 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6846 *
6847 * @since 3.2
6848 **/
6849 public function implode_frontend_css( $travis_test = false ) {
6850 $do_implode = true;
6851 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6852 $do_implode = false;
6853 }
6854
6855 // Do not implode CSS when the page loads via the AMP plugin.
6856 if ( Jetpack_AMP_Support::is_amp_request() ) {
6857 $do_implode = false;
6858 }
6859
6860 /**
6861 * Allow CSS to be concatenated into a single jetpack.css file.
6862 *
6863 * @since 3.2.0
6864 *
6865 * @param bool $do_implode Should CSS be concatenated? Default to true.
6866 */
6867 $do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6868
6869 // Do not use the imploded file when default behavior was altered through the filter
6870 if ( ! $do_implode ) {
6871 return;
6872 }
6873
6874 // We do not want to use the imploded file in dev mode, or if not connected
6875 if ( ( new Status() )->is_offline_mode() || ! self::is_connection_ready() ) {
6876 if ( ! $travis_test ) {
6877 return;
6878 }
6879 }
6880
6881 // Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6882 if ( get_option( 'sharedaddy_disable_resources' ) ) {
6883 return;
6884 }
6885
6886 /*
6887 * Now we assume Jetpack is connected and able to serve the single
6888 * file.
6889 *
6890 * In the future there will be a check here to serve the file locally
6891 * or potentially from the Jetpack CDN
6892 *
6893 * For now:
6894 * - Enqueue a single imploded css file
6895 * - Zero out the style_loader_tag for the bundled ones
6896 * - Be happy, drink scotch
6897 */
6898
6899 add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6900
6901 $version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6902
6903 wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6904 wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6905 }
6906
6907 function concat_remove_style_loader_tag( $tag, $handle ) {
6908 if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6909 $tag = '';
6910 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6911 $tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6912 }
6913 }
6914
6915 return $tag;
6916 }
6917
6918 /**
6919 * @deprecated
6920 * @see Automattic\Jetpack\Assets\add_aync_script
6921 */
6922 public function script_add_async( $tag, $handle, $src ) {
6923 _deprecated_function( __METHOD__, 'jetpack-8.6.0' );
6924 }
6925
6926 /*
6927 * Check the heartbeat data
6928 *
6929 * Organizes the heartbeat data by severity. For example, if the site
6930 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6931 *
6932 * Data will be added to "caution" array, if it either:
6933 * - Out of date Jetpack version
6934 * - Out of date WP version
6935 * - Out of date PHP version
6936 *
6937 * $return array $filtered_data
6938 */
6939 public static function jetpack_check_heartbeat_data() {
6940 $raw_data = Jetpack_Heartbeat::generate_stats_array();
6941
6942 $good = array();
6943 $caution = array();
6944 $bad = array();
6945
6946 foreach ( $raw_data as $stat => $value ) {
6947
6948 // Check jetpack version
6949 if ( 'version' == $stat ) {
6950 if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6951 $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__VERSION;
6952 continue;
6953 }
6954 }
6955
6956 // Check WP version
6957 if ( 'wp-version' == $stat ) {
6958 if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6959 $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_WP_VERSION;
6960 continue;
6961 }
6962 }
6963
6964 // Check PHP version
6965 if ( 'php-version' == $stat ) {
6966 if ( version_compare( PHP_VERSION, JETPACK__MINIMUM_PHP_VERSION, '<' ) ) {
6967 $caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_PHP_VERSION;
6968 continue;
6969 }
6970 }
6971
6972 // Check ID crisis
6973 if ( 'identitycrisis' == $stat ) {
6974 if ( 'yes' == $value ) {
6975 $bad[ $stat ] = $value;
6976 continue;
6977 }
6978 }
6979
6980 // The rest are good :)
6981 $good[ $stat ] = $value;
6982 }
6983
6984 $filtered_data = array(
6985 'good' => $good,
6986 'caution' => $caution,
6987 'bad' => $bad,
6988 );
6989
6990 return $filtered_data;
6991 }
6992
6993 /*
6994 * This method is used to organize all options that can be reset
6995 * without disconnecting Jetpack.
6996 *
6997 * It is used in class.jetpack-cli.php to reset options
6998 *
6999 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
7000 *
7001 * @return array of options to delete.
7002 */
7003 public static function get_jetpack_options_for_reset() {
7004 return Jetpack_Options::get_options_for_reset();
7005 }
7006
7007 /*
7008 * Strip http:// or https:// from a url, replaces forward slash with ::,
7009 * so we can bring them directly to their site in calypso.
7010 *
7011 * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
7012 *
7013 * @param string | url
7014 * @return string | url without the guff
7015 */
7016 public static function build_raw_urls( $url ) {
7017 _deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
7018
7019 return ( new Status() )->get_site_suffix( $url );
7020 }
7021
7022 /**
7023 * Stores and prints out domains to prefetch for page speed optimization.
7024 *
7025 * @deprecated 8.8.0 Use Jetpack::add_resource_hints.
7026 *
7027 * @param string|array $urls URLs to hint.
7028 */
7029 public static function dns_prefetch( $urls = null ) {
7030 _deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Automattic\Jetpack\Assets::add_resource_hint' );
7031 if ( $urls ) {
7032 Assets::add_resource_hint( $urls );
7033 }
7034 }
7035
7036 /**
7037 * @param mixed $result Value for the user's option
7038 * @return mixed
7039 */
7040 function get_user_option_meta_box_order_dashboard( $sorted ) {
7041 if ( ! is_array( $sorted ) ) {
7042 return $sorted;
7043 }
7044
7045 foreach ( $sorted as $box_context => $ids ) {
7046 if ( false === strpos( $ids, 'dashboard_stats' ) ) {
7047 // If the old id isn't anywhere in the ids, don't bother exploding and fail out.
7048 continue;
7049 }
7050
7051 $ids_array = explode( ',', $ids );
7052 $key = array_search( 'dashboard_stats', $ids_array );
7053
7054 if ( false !== $key ) {
7055 // If we've found that exact value in the option (and not `google_dashboard_stats` for example)
7056 $ids_array[ $key ] = 'jetpack_summary_widget';
7057 $sorted[ $box_context ] = implode( ',', $ids_array );
7058 // We've found it, stop searching, and just return.
7059 break;
7060 }
7061 }
7062
7063 return $sorted;
7064 }
7065
7066 /*
7067 * Adds a "blank" column in the user admin table to display indication of user connection.
7068 */
7069 function jetpack_icon_user_connected( $columns ) {
7070 $columns['user_jetpack'] = '';
7071 return $columns;
7072 }
7073
7074 /*
7075 * Show Jetpack icon if the user is linked.
7076 */
7077 function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
7078 if ( 'user_jetpack' === $col && self::connection()->is_user_connected( $user_id ) ) {
7079 $jetpack_logo = new Jetpack_Logo();
7080 $emblem_html = sprintf(
7081 '<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
7082 esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
7083 $jetpack_logo->get_jp_emblem()
7084 );
7085 return $emblem_html;
7086 }
7087
7088 return $val;
7089 }
7090
7091 /*
7092 * Style the Jetpack user column
7093 */
7094 function jetpack_user_col_style() {
7095 global $current_screen;
7096 if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) {
7097 ?>
7098 <style>
7099 .fixed .column-user_jetpack {
7100 width: 21px;
7101 }
7102 .jp-emblem-user-admin svg {
7103 width: 20px;
7104 height: 20px;
7105 }
7106 .jp-emblem-user-admin path {
7107 fill: #00BE28;
7108 }
7109 </style>
7110 <?php
7111 }
7112 }
7113
7114 /**
7115 * Checks if Akismet is active and working.
7116 *
7117 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
7118 * that implied usage of methods present since more recent version.
7119 * See https://github.com/Automattic/jetpack/pull/9585
7120 *
7121 * @since 5.1.0
7122 *
7123 * @return bool True = Akismet available. False = Aksimet not available.
7124 */
7125 public static function is_akismet_active() {
7126 static $status = null;
7127
7128 if ( ! is_null( $status ) ) {
7129 return $status;
7130 }
7131
7132 // Check if a modern version of Akismet is active.
7133 if ( ! method_exists( 'Akismet', 'http_post' ) ) {
7134 $status = false;
7135 return $status;
7136 }
7137
7138 // Make sure there is a key known to Akismet at all before verifying key.
7139 $akismet_key = Akismet::get_api_key();
7140 if ( ! $akismet_key ) {
7141 $status = false;
7142 return $status;
7143 }
7144
7145 // Possible values: valid, invalid, failure via Akismet. false if no status is cached.
7146 $akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
7147
7148 // Do not used the cache result in wp-admin or REST API requests if the key isn't valid, in case someone is actively renewing, etc.
7149 $recheck = ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state;
7150 // We cache the result of the Akismet key verification for ten minutes.
7151 if ( ! $akismet_key_state || $recheck ) {
7152 $akismet_key_state = Akismet::verify_key( $akismet_key );
7153 set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
7154 }
7155
7156 $status = 'valid' === $akismet_key_state;
7157
7158 return $status;
7159 }
7160
7161 /**
7162 * @deprecated
7163 *
7164 * @see Automattic\Jetpack\Sync\Modules\Users::is_function_in_backtrace
7165 */
7166 public static function is_function_in_backtrace() {
7167 _deprecated_function( __METHOD__, 'jetpack-7.6.0' );
7168 }
7169
7170 /**
7171 * Given a minified path, and a non-minified path, will return
7172 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
7173 *
7174 * Both `$min_base` and `$non_min_base` are expected to be relative to the
7175 * root Jetpack directory.
7176 *
7177 * @since 5.6.0
7178 *
7179 * @param string $min_path
7180 * @param string $non_min_path
7181 * @return string The URL to the file
7182 */
7183 public static function get_file_url_for_environment( $min_path, $non_min_path ) {
7184 return Assets::get_file_url_for_environment( $min_path, $non_min_path );
7185 }
7186
7187 /**
7188 * Checks for whether Jetpack Backup is enabled.
7189 * Will return true if the state of Backup is anything except "unavailable".
7190 *
7191 * @return bool|int|mixed
7192 */
7193 public static function is_rewind_enabled() {
7194 // Rewind is a paid feature, therefore requires a user-level connection.
7195 if ( ! static::connection()->has_connected_owner() ) {
7196 return false;
7197 }
7198
7199 $rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7200 if ( false === $rewind_enabled ) {
7201 jetpack_require_lib( 'class.core-rest-api-endpoints' );
7202 $rewind_data = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7203 $rewind_enabled = ( ! is_wp_error( $rewind_data )
7204 && ! empty( $rewind_data['state'] )
7205 && 'active' === $rewind_data['state'] )
7206 ? 1
7207 : 0;
7208
7209 set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7210 }
7211 return $rewind_enabled;
7212 }
7213
7214 /**
7215 * Return Calypso environment value; used for developing Jetpack and pairing
7216 * it with different Calypso enrionments, such as localhost.
7217 *
7218 * @since 7.4.0
7219 *
7220 * @return string Calypso environment
7221 */
7222 public static function get_calypso_env() {
7223 if ( isset( $_GET['calypso_env'] ) ) {
7224 return sanitize_key( $_GET['calypso_env'] );
7225 }
7226
7227 if ( getenv( 'CALYPSO_ENV' ) ) {
7228 return sanitize_key( getenv( 'CALYPSO_ENV' ) );
7229 }
7230
7231 if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
7232 return sanitize_key( CALYPSO_ENV );
7233 }
7234
7235 return '';
7236 }
7237
7238 /**
7239 * Returns the hostname with protocol for Calypso.
7240 * Used for developing Jetpack with Calypso.
7241 *
7242 * @since 8.4.0
7243 *
7244 * @return string Calypso host.
7245 */
7246 public static function get_calypso_host() {
7247 $calypso_env = self::get_calypso_env();
7248 switch ( $calypso_env ) {
7249 case 'development':
7250 return 'http://calypso.localhost:3000/';
7251 case 'wpcalypso':
7252 return 'https://wpcalypso.wordpress.com/';
7253 case 'horizon':
7254 return 'https://horizon.wordpress.com/';
7255 default:
7256 return 'https://wordpress.com/';
7257 }
7258 }
7259
7260 /**
7261 * Handles activating default modules as well general cleanup for the new connection.
7262 *
7263 * @param boolean $activate_sso Whether to activate the SSO module when activating default modules.
7264 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7265 * @param boolean $send_state_messages Whether to send state messages.
7266 * @return void
7267 */
7268 public static function handle_post_authorization_actions(
7269 $activate_sso = false,
7270 $redirect_on_activation_error = false,
7271 $send_state_messages = true
7272 ) {
7273 $other_modules = $activate_sso
7274 ? array( 'sso' )
7275 : array();
7276
7277 if ( Jetpack_Options::get_option( 'active_modules_initialized' ) ) {
7278 $active_modules = Jetpack_Options::get_option( 'active_modules' );
7279 self::delete_active_modules();
7280
7281 self::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, $send_state_messages );
7282 } else {
7283 // Default modules that don't require a user were already activated on site_register.
7284 // 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.
7285 self::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages, null, true );
7286 Jetpack_Options::update_option( 'active_modules_initialized', true );
7287 }
7288
7289 // Since this is a fresh connection, be sure to clear out IDC options
7290 Identity_Crisis::clear_all_idc_options();
7291
7292 if ( $send_state_messages ) {
7293 self::state( 'message', 'authorized' );
7294 }
7295 }
7296
7297 /**
7298 * Returns a boolean for whether backups UI should be displayed or not.
7299 *
7300 * @return bool Should backups UI be displayed?
7301 */
7302 public static function show_backups_ui() {
7303 /**
7304 * Whether UI for backups should be displayed.
7305 *
7306 * @since 6.5.0
7307 *
7308 * @param bool $show_backups Should UI for backups be displayed? True by default.
7309 */
7310 return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7311 }
7312
7313 /*
7314 * Deprecated manage functions
7315 */
7316 function prepare_manage_jetpack_notice() {
7317 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7318 }
7319 function manage_activate_screen() {
7320 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7321 }
7322 function admin_jetpack_manage_notice() {
7323 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7324 }
7325 function opt_out_jetpack_manage_url() {
7326 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7327 }
7328 function opt_in_jetpack_manage_url() {
7329 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7330 }
7331 function opt_in_jetpack_manage_notice() {
7332 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7333 }
7334 function can_display_jetpack_manage_notice() {
7335 _deprecated_function( __METHOD__, 'jetpack-7.3' );
7336 }
7337
7338 /**
7339 * Clean leftoveruser meta.
7340 *
7341 * Delete Jetpack-related user meta when it is no longer needed.
7342 *
7343 * @since 7.3.0
7344 *
7345 * @param int $user_id User ID being updated.
7346 */
7347 public static function user_meta_cleanup( $user_id ) {
7348 $meta_keys = array(
7349 // AtD removed from Jetpack 7.3
7350 'AtD_options',
7351 'AtD_check_when',
7352 'AtD_guess_lang',
7353 'AtD_ignored_phrases',
7354 );
7355
7356 foreach ( $meta_keys as $meta_key ) {
7357 if ( get_user_meta( $user_id, $meta_key ) ) {
7358 delete_user_meta( $user_id, $meta_key );
7359 }
7360 }
7361 }
7362
7363 /**
7364 * Checks if a Jetpack site is both active and not in offline mode.
7365 *
7366 * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_offline_mode`.
7367 *
7368 * @deprecated 8.8.0
7369 *
7370 * @return bool True if Jetpack is active and not in offline mode.
7371 */
7372 public static function is_active_and_not_development_mode() {
7373 _deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Jetpack::is_active_and_not_offline_mode' );
7374 if ( ! self::is_active() || ( new Status() )->is_offline_mode() ) {
7375 return false;
7376 }
7377 return true;
7378 }
7379
7380 /**
7381 * Checks if a Jetpack site is both active and not in offline mode.
7382 *
7383 * This is a DRY function to avoid repeating `Jetpack::is_connection_ready && ! Automattic\Jetpack\Status->is_offline_mode`.
7384 *
7385 * @since 8.8.0
7386 *
7387 * @return bool True if Jetpack is active and not in offline mode.
7388 */
7389 public static function is_active_and_not_offline_mode() {
7390 if ( ! self::is_connection_ready() || ( new Status() )->is_offline_mode() ) {
7391 return false;
7392 }
7393 return true;
7394 }
7395
7396 /**
7397 * Returns the list of products that we have available for purchase.
7398 */
7399 public static function get_products_for_purchase() {
7400 $products = array();
7401 if ( ! is_multisite() ) {
7402 $products[] = array(
7403 'key' => 'backup',
7404 'title' => __( 'Jetpack Backup', 'jetpack' ),
7405 'short_description' => __( 'Always-on backups ensure you never lose your site.', 'jetpack' ),
7406 'learn_more' => __( 'Which backup option is best for me?', 'jetpack' ),
7407 'description' => __( 'Always-on backups ensure you never lose your site. Your changes are saved as you edit and you have unlimited backup archives.', 'jetpack' ),
7408 'options_label' => __( 'Select a backup option:', 'jetpack' ),
7409 'options' => array(
7410 array(
7411 'type' => 'daily',
7412 'slug' => 'jetpack-backup-daily',
7413 'key' => 'jetpack_backup_daily',
7414 'name' => __( 'Daily Backups', 'jetpack' ),
7415 'description' => __( 'Your data is being securely backed up daily.', 'jetpack' ),
7416 ),
7417 array(
7418 'type' => 'realtime',
7419 'slug' => 'jetpack-backup-realtime',
7420 'key' => 'jetpack_backup_realtime',
7421 'name' => __( 'Real-Time Backups', 'jetpack' ),
7422 'description' => __( 'Your data is being securely backed up as you edit.', 'jetpack' ),
7423 ),
7424 ),
7425 'default_option' => 'realtime',
7426 'show_promotion' => true,
7427 'discount_percent' => 70,
7428 'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'daily-backup-plan', 'realtime-backup-plan' ),
7429 );
7430
7431 $products[] = array(
7432 'key' => 'scan',
7433 'title' => __( 'Jetpack Scan', 'jetpack' ),
7434 'short_description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7435 'learn_more' => __( 'Learn More', 'jetpack' ),
7436 'description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7437 'show_promotion' => true,
7438 'discount_percent' => 30,
7439 'options' => array(
7440 array(
7441 'type' => 'scan',
7442 'slug' => 'jetpack-scan',
7443 'key' => 'jetpack_scan',
7444 'name' => __( 'Daily Scan', 'jetpack' ),
7445 ),
7446 ),
7447 'default_option' => 'scan',
7448 'included_in_plans' => array( 'premium-plan', 'business-plan', 'scan-plan' ),
7449 );
7450 }
7451
7452 $products[] = array(
7453 'key' => 'search',
7454 'title' => __( 'Jetpack Search', 'jetpack' ),
7455 'short_description' => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7456 'learn_more' => __( 'Learn More', 'jetpack' ),
7457 'description' => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7458 'label_popup' => __( 'Records are all posts, pages, custom post types, and other types of content indexed by Jetpack Search.', 'jetpack' ),
7459 'options' => array(
7460 array(
7461 'type' => 'search',
7462 'slug' => 'jetpack-search',
7463 'key' => 'jetpack_search',
7464 'name' => __( 'Search', 'jetpack' ),
7465 ),
7466 ),
7467 'tears' => array(),
7468 'default_option' => 'search',
7469 'show_promotion' => false,
7470 'included_in_plans' => array( 'search-plan' ),
7471 );
7472
7473 $products[] = array(
7474 'key' => 'anti-spam',
7475 'title' => __( 'Jetpack Anti-Spam', 'jetpack' ),
7476 'short_description' => __( 'Automatically clear spam from comments and forms. Save time, get more responses, give your visitors a better experience – all without lifting a finger.', 'jetpack' ),
7477 'learn_more' => __( 'Learn More', 'jetpack' ),
7478 'description' => __( 'Automatically clear spam from comments and forms. Save time, get more responses, give your visitors a better experience – all without lifting a finger.', 'jetpack' ),
7479 'options' => array(
7480 array(
7481 'type' => 'anti-spam',
7482 'slug' => 'jetpack-anti-spam',
7483 'key' => 'jetpack_anti_spam',
7484 'name' => __( 'Anti-Spam', 'jetpack' ),
7485 ),
7486 ),
7487 'default_option' => 'anti-spam',
7488 'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'anti-spam-plan' ),
7489 );
7490
7491 return $products;
7492 }
7493
7494 /**
7495 * Determine if the current user is allowed to make Jetpack purchases without
7496 * a WordPress.com account
7497 *
7498 * @return boolean True if the user can make purchases, false if not
7499 */
7500 public static function current_user_can_purchase() {
7501
7502 // The site must be site-connected to Jetpack (no users connected).
7503 if ( ! self::connection()->is_site_connection() ) {
7504 return false;
7505 }
7506
7507 // Make sure only administrators can make purchases.
7508 if ( ! current_user_can( 'manage_options' ) ) {
7509 return false;
7510 }
7511
7512 return true;
7513 }
7514
7515 }
7516