PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.9.3
Jetpack – WP Security, Backup, Speed, & Growth v9.9.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 9.9.3, at class.jetpack.php

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