PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.2.3
Jetpack – WP Security, Backup, Speed, & Growth v10.2.3
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.3, at class.jetpack.php

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