Completed
Push — add/connection-ui ( 1cf4ba...eabe61 )
by
unknown
15:51 queued 07:50
created

class.jetpack.php (77 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
use Automattic\Jetpack\Assets;
3
use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
4
use Automattic\Jetpack\Config;
5
use Automattic\Jetpack\Connection\Client;
6
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
7
use Automattic\Jetpack\Connection\Plugin_Storage as Connection_Plugin_Storage;
8
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
9
use Automattic\Jetpack\Connection\Utils as Connection_Utils;
10
use Automattic\Jetpack\Constants;
11
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
12
use Automattic\Jetpack\Licensing;
13
use Automattic\Jetpack\Partner;
14
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
15
use Automattic\Jetpack\Redirect;
16
use Automattic\Jetpack\Roles;
17
use Automattic\Jetpack\Status;
18
use Automattic\Jetpack\Sync\Functions;
19
use Automattic\Jetpack\Sync\Health;
20
use Automattic\Jetpack\Sync\Sender;
21
use Automattic\Jetpack\Sync\Users;
22
use Automattic\Jetpack\Terms_Of_Service;
23
use Automattic\Jetpack\Tracking;
24
25
/*
26
Options:
27
jetpack_options (array)
28
	An array of options.
29
	@see Jetpack_Options::get_option_names()
30
31
jetpack_register (string)
32
	Temporary verification secrets.
33
34
jetpack_activated (int)
35
	1: the plugin was activated normally
36
	2: the plugin was activated on this site because of a network-wide activation
37
	3: the plugin was auto-installed
38
	4: the plugin was manually disconnected (but is still installed)
39
40
jetpack_active_modules (array)
41
	Array of active module slugs.
42
43
jetpack_do_activate (bool)
44
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
45
*/
46
47
require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php';
48
49
class Jetpack {
50
	public $xmlrpc_server = null;
51
52
	/**
53
	 * @var array The handles of styles that are concatenated into jetpack.css.
54
	 *
55
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
56
	 */
57
	public $concatenated_style_handles = array(
58
		'jetpack-carousel',
59
		'grunion.css',
60
		'the-neverending-homepage',
61
		'jetpack_likes',
62
		'jetpack_related-posts',
63
		'sharedaddy',
64
		'jetpack-slideshow',
65
		'presentations',
66
		'quiz',
67
		'jetpack-subscriptions',
68
		'jetpack-responsive-videos-style',
69
		'jetpack-social-menu',
70
		'tiled-gallery',
71
		'jetpack_display_posts_widget',
72
		'gravatar-profile-widget',
73
		'goodreads-widget',
74
		'jetpack_social_media_icons_widget',
75
		'jetpack-top-posts-widget',
76
		'jetpack_image_widget',
77
		'jetpack-my-community-widget',
78
		'jetpack-authors-widget',
79
		'wordads',
80
		'eu-cookie-law-style',
81
		'flickr-widget-style',
82
		'jetpack-search-widget',
83
		'jetpack-simple-payments-widget-style',
84
		'jetpack-widget-social-icons-styles',
85
		'wpcom_instagram_widget',
86
	);
87
88
	/**
89
	 * Contains all assets that have had their URL rewritten to minified versions.
90
	 *
91
	 * @var array
92
	 */
93
	static $min_assets = array();
94
95
	public $plugins_to_deactivate = array(
96
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
97
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
98
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
99
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
100
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
101
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
102
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
103
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
104
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
105
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
106
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
107
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
108
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
109
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' ),
110
	);
111
112
	/**
113
	 * Map of roles we care about, and their corresponding minimum capabilities.
114
	 *
115
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::$capability_translations instead.
116
	 *
117
	 * @access public
118
	 * @static
119
	 *
120
	 * @var array
121
	 */
122
	public static $capability_translations = array(
123
		'administrator' => 'manage_options',
124
		'editor'        => 'edit_others_posts',
125
		'author'        => 'publish_posts',
126
		'contributor'   => 'edit_posts',
127
		'subscriber'    => 'read',
128
	);
129
130
	/**
131
	 * Map of modules that have conflicts with plugins and should not be auto-activated
132
	 * if the plugins are active.  Used by filter_default_modules
133
	 *
134
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
135
	 * change `module-slug` and add this to your plugin:
136
	 *
137
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
138
	 * function my_jetpack_get_default_modules( $modules ) {
139
	 *     return array_diff( $modules, array( 'module-slug' ) );
140
	 * }
141
	 *
142
	 * @var array
143
	 */
144
	private $conflicting_plugins = array(
145
		'comments'           => array(
146
			'Intense Debate'                 => 'intensedebate/intensedebate.php',
147
			'Disqus'                         => 'disqus-comment-system/disqus.php',
148
			'Livefyre'                       => 'livefyre-comments/livefyre.php',
149
			'Comments Evolved for WordPress' => 'gplus-comments/comments-evolved.php',
150
			'Google+ Comments'               => 'google-plus-comments/google-plus-comments.php',
151
			'WP-SpamShield Anti-Spam'        => 'wp-spamshield/wp-spamshield.php',
152
		),
153
		'comment-likes'      => array(
154
			'Epoch' => 'epoch/plugincore.php',
155
		),
156
		'contact-form'       => array(
157
			'Contact Form 7'           => 'contact-form-7/wp-contact-form-7.php',
158
			'Gravity Forms'            => 'gravityforms/gravityforms.php',
159
			'Contact Form Plugin'      => 'contact-form-plugin/contact_form.php',
160
			'Easy Contact Forms'       => 'easy-contact-forms/easy-contact-forms.php',
161
			'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php',
162
			'Ninja Forms'              => 'ninja-forms/ninja-forms.php',
163
		),
164
		'latex'              => array(
165
			'LaTeX for WordPress'     => 'latex/latex.php',
166
			'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php',
167
			'Easy WP LaTeX'           => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
168
			'MathJax-LaTeX'           => 'mathjax-latex/mathjax-latex.php',
169
			'Enable Latex'            => 'enable-latex/enable-latex.php',
170
			'WP QuickLaTeX'           => 'wp-quicklatex/wp-quicklatex.php',
171
		),
172
		'protect'            => array(
173
			'Limit Login Attempts'              => 'limit-login-attempts/limit-login-attempts.php',
174
			'Captcha'                           => 'captcha/captcha.php',
175
			'Brute Force Login Protection'      => 'brute-force-login-protection/brute-force-login-protection.php',
176
			'Login Security Solution'           => 'login-security-solution/login-security-solution.php',
177
			'WPSecureOps Brute Force Protect'   => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
178
			'BulletProof Security'              => 'bulletproof-security/bulletproof-security.php',
179
			'SiteGuard WP Plugin'               => 'siteguard/siteguard.php',
180
			'Security-protection'               => 'security-protection/security-protection.php',
181
			'Login Security'                    => 'login-security/login-security.php',
182
			'Botnet Attack Blocker'             => 'botnet-attack-blocker/botnet-attack-blocker.php',
183
			'Wordfence Security'                => 'wordfence/wordfence.php',
184
			'All In One WP Security & Firewall' => 'all-in-one-wp-security-and-firewall/wp-security.php',
185
			'iThemes Security'                  => 'better-wp-security/better-wp-security.php',
186
		),
187
		'random-redirect'    => array(
188
			'Random Redirect 2' => 'random-redirect-2/random-redirect.php',
189
		),
190
		'related-posts'      => array(
191
			'YARPP'                       => 'yet-another-related-posts-plugin/yarpp.php',
192
			'WordPress Related Posts'     => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
193
			'nrelate Related Content'     => 'nrelate-related-content/nrelate-related.php',
194
			'Contextual Related Posts'    => 'contextual-related-posts/contextual-related-posts.php',
195
			'Related Posts for WordPress' => 'microkids-related-posts/microkids-related-posts.php',
196
			'outbrain'                    => 'outbrain/outbrain.php',
197
			'Shareaholic'                 => 'shareaholic/shareaholic.php',
198
			'Sexybookmarks'               => 'sexybookmarks/shareaholic.php',
199
		),
200
		'sharedaddy'         => array(
201
			'AddThis'     => 'addthis/addthis_social_widget.php',
202
			'Add To Any'  => 'add-to-any/add-to-any.php',
203
			'ShareThis'   => 'share-this/sharethis.php',
204
			'Shareaholic' => 'shareaholic/shareaholic.php',
205
		),
206
		'seo-tools'          => array(
207
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
208
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
209
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
210
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
211
			'The SEO Framework'              => 'autodescription/autodescription.php',
212
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
213
			'Slim SEO'                       => 'slim-seo/slim-seo.php',
214
		),
215
		'verification-tools' => array(
216
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
217
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
218
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
219
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
220
			'The SEO Framework'              => 'autodescription/autodescription.php',
221
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
222
			'Slim SEO'                       => 'slim-seo/slim-seo.php',
223
		),
224
		'widget-visibility'  => array(
225
			'Widget Logic'    => 'widget-logic/widget_logic.php',
226
			'Dynamic Widgets' => 'dynamic-widgets/dynamic-widgets.php',
227
		),
228
		'sitemaps'           => array(
229
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
230
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
231
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
232
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
233
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
234
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
235
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
236
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
237
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
238
			'The SEO Framework'                    => 'autodescription/autodescription.php',
239
			'Sitemap'                              => 'sitemap/sitemap.php',
240
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
241
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
242
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
243
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
244
			'Rank Math'                            => 'seo-by-rank-math/rank-math.php',
245
			'Slim SEO'                             => 'slim-seo/slim-seo.php',
246
		),
247
		'lazy-images'        => array(
248
			'Lazy Load'              => 'lazy-load/lazy-load.php',
249
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
250
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
251
		),
252
	);
253
254
	/**
255
	 * Plugins for which we turn off our Facebook OG Tags implementation.
256
	 *
257
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
258
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
259
	 *
260
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
261
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
262
	 */
263
	private $open_graph_conflicting_plugins = array(
264
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
265
		// 2 Click Social Media Buttons
266
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
267
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
268
		'complete-open-graph/complete-open-graph.php',           // Complete Open Graph
269
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
270
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
271
		// Open Graph Meta Tags by Heateor
272
		'facebook/facebook.php',                                 // Facebook (official plugin)
273
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
274
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
275
		// Facebook Featured Image & OG Meta Tags
276
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
277
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
278
		// Facebook Open Graph Meta Tags for WordPress
279
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
280
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
281
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
282
		// Fedmich's Facebook Open Graph Meta
283
		'network-publisher/networkpub.php',                      // Network Publisher
284
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
285
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
286
		// NextScripts SNAP
287
		'og-tags/og-tags.php',                                   // OG Tags
288
		'opengraph/opengraph.php',                               // Open Graph
289
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
290
		// Open Graph Protocol Framework
291
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
292
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
293
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
294
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
295
		'sharepress/sharepress.php',                             // SharePress
296
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
297
		'social-discussions/social-discussions.php',             // Social Discussions
298
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
299
		'socialize/socialize.php',                               // Socialize
300
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
301
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
302
		// Tweet, Like, Google +1 and Share
303
		'wordbooker/wordbooker.php',                             // Wordbooker
304
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
305
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
306
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
307
		// WP Facebook Like Send & Open Graph Meta
308
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
309
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
310
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
311
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
312
		'open-graph-metabox/open-graph-metabox.php',              // Open Graph Metabox
313
		'seo-by-rank-math/rank-math.php',                        // Rank Math.
314
		'slim-seo/slim-seo.php',                                 // Slim SEO
315
	);
316
317
	/**
318
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
319
	 */
320
	private $twitter_cards_conflicting_plugins = array(
321
		// 'twitter/twitter.php',                       // The official one handles this on its own.
322
		// https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
323
			'eewee-twitter-card/index.php',              // Eewee Twitter Card
324
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
325
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
326
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
327
		// Pure Web Brilliant's Social Graph Twitter Cards Extension
328
		'twitter-cards/twitter-cards.php',           // Twitter Cards
329
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
330
		'wp-to-twitter/wp-to-twitter.php',           // WP to Twitter
331
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
332
		'seo-by-rank-math/rank-math.php',            // Rank Math.
333
		'slim-seo/slim-seo.php',                     // Slim SEO
334
	);
335
336
	/**
337
	 * Message to display in admin_notice
338
	 *
339
	 * @var string
340
	 */
341
	public $message = '';
342
343
	/**
344
	 * Error to display in admin_notice
345
	 *
346
	 * @var string
347
	 */
348
	public $error = '';
349
350
	/**
351
	 * Modules that need more privacy description.
352
	 *
353
	 * @var string
354
	 */
355
	public $privacy_checks = '';
356
357
	/**
358
	 * Stats to record once the page loads
359
	 *
360
	 * @var array
361
	 */
362
	public $stats = array();
363
364
	/**
365
	 * Jetpack_Sync object
366
	 */
367
	public $sync;
368
369
	/**
370
	 * Verified data for JSON authorization request
371
	 */
372
	public $json_api_authorization_request = array();
373
374
	/**
375
	 * @var Automattic\Jetpack\Connection\Manager
376
	 */
377
	protected $connection_manager;
378
379
	/**
380
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
381
	 */
382
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
383
384
	/**
385
	 * Holds an instance of Automattic\Jetpack\A8c_Mc_Stats
386
	 *
387
	 * @var Automattic\Jetpack\A8c_Mc_Stats
388
	 */
389
	public $a8c_mc_stats_instance;
390
391
	/**
392
	 * Constant for login redirect key.
393
	 *
394
	 * @var string
395
	 * @since 8.4.0
396
	 */
397
	public static $jetpack_redirect_login = 'jetpack_connect_login_redirect';
398
399
	/**
400
	 * Holds the singleton instance of this class
401
	 *
402
	 * @since 2.3.3
403
	 * @var Jetpack
404
	 */
405
	static $instance = false;
406
407
	/**
408
	 * Singleton
409
	 *
410
	 * @static
411
	 */
412
	public static function init() {
413
		if ( ! self::$instance ) {
414
			self::$instance = new Jetpack();
415
			add_action( 'plugins_loaded', array( self::$instance, 'plugin_upgrade' ) );
416
		}
417
418
		return self::$instance;
419
	}
420
421
	/**
422
	 * Must never be called statically
423
	 */
424
	function plugin_upgrade() {
425
		if ( self::is_active() ) {
426
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
427
			if ( JETPACK__VERSION != $version ) {
428
				// Prevent multiple upgrades at once - only a single process should trigger
429
				// an upgrade to avoid stampedes
430
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
431
					return;
432
				}
433
434
				// Set a short lock to prevent multiple instances of the upgrade
435
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
436
437
				// check which active modules actually exist and remove others from active_modules list
438
				$unfiltered_modules = self::get_active_modules();
439
				$modules            = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
440
				if ( array_diff( $unfiltered_modules, $modules ) ) {
441
					self::update_active_modules( $modules );
442
				}
443
444
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
445
446
				// Upgrade to 4.3.0
447
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
448
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
449
				}
450
451
				// Make sure Markdown for posts gets turned back on
452
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
453
					update_option( 'wpcom_publish_posts_with_markdown', true );
454
				}
455
456
				/*
457
				 * Minileven deprecation. 8.3.0.
458
				 * Only delete options if not using
459
				 * the replacement standalone Minileven plugin.
460
				 */
461
				if (
462
					! self::is_plugin_active( 'minileven-master/minileven.php' )
463
					&& ! self::is_plugin_active( 'minileven/minileven.php' )
464
				) {
465
					if ( get_option( 'wp_mobile_custom_css' ) ) {
466
						delete_option( 'wp_mobile_custom_css' );
467
					}
468
					if ( get_option( 'wp_mobile_excerpt' ) ) {
469
						delete_option( 'wp_mobile_excerpt' );
470
					}
471
					if ( get_option( 'wp_mobile_featured_images' ) ) {
472
						delete_option( 'wp_mobile_featured_images' );
473
					}
474
					if ( get_option( 'wp_mobile_app_promos' ) ) {
475
						delete_option( 'wp_mobile_app_promos' );
476
					}
477
				}
478
479
				// Upgrade to 8.4.0.
480
				if ( Jetpack_Options::get_option( 'ab_connect_banner_green_bar' ) ) {
481
					Jetpack_Options::delete_option( 'ab_connect_banner_green_bar' );
482
				}
483
484
				// Update to 8.8.x (WordPress 5.5 Compatibility).
485
				if ( Jetpack_Options::get_option( 'autoupdate_plugins' ) ) {
486
					$updated = update_site_option(
487
						'auto_update_plugins',
488
						array_unique(
489
							array_merge(
490
								(array) Jetpack_Options::get_option( 'autoupdate_plugins', array() ),
491
								(array) get_site_option( 'auto_update_plugins', array() )
492
							)
493
						)
494
					);
495
496
					if ( $updated ) {
497
						Jetpack_Options::delete_option( 'autoupdate_plugins' );
498
					} // Should we have some type of fallback if something fails here?
499
				}
500
501
				if ( did_action( 'wp_loaded' ) ) {
502
					self::upgrade_on_load();
503
				} else {
504
					add_action(
505
						'wp_loaded',
506
						array( __CLASS__, 'upgrade_on_load' )
507
					);
508
				}
509
			}
510
		}
511
	}
512
513
	/**
514
	 * Runs upgrade routines that need to have modules loaded.
515
	 */
516
	static function upgrade_on_load() {
517
518
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
519
		// This can happen in case Jetpack has been just upgraded and is
520
		// being initialized late during the page load. In this case we wait
521
		// until the next proper admin page load with Jetpack active.
522
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
523
			delete_transient( self::$plugin_upgrade_lock_key );
524
525
			return;
526
		}
527
528
		self::maybe_set_version_option();
529
530
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
531
			Jetpack_Widget_Conditions::migrate_post_type_rules();
532
		}
533
534
		if (
535
			class_exists( 'Jetpack_Sitemap_Manager' )
536
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
537
		) {
538
			do_action( 'jetpack_sitemaps_purge_data' );
539
		}
540
541
		// Delete old stats cache
542
		delete_option( 'jetpack_restapi_stats_cache' );
543
544
		delete_transient( self::$plugin_upgrade_lock_key );
545
	}
546
547
	/**
548
	 * Saves all the currently active modules to options.
549
	 * Also fires Action hooks for each newly activated and deactivated module.
550
	 *
551
	 * @param $modules Array Array of active modules to be saved in options.
552
	 *
553
	 * @return $success bool true for success, false for failure.
0 ignored issues
show
The doc-type $success could not be parsed: Unknown type name "$success" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
554
	 */
555
	static function update_active_modules( $modules ) {
556
		$current_modules      = Jetpack_Options::get_option( 'active_modules', array() );
557
		$active_modules       = self::get_active_modules();
558
		$new_active_modules   = array_diff( $modules, $current_modules );
559
		$new_inactive_modules = array_diff( $active_modules, $modules );
560
		$new_current_modules  = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
561
		$reindexed_modules    = array_values( $new_current_modules );
562
		$success              = Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
563
564
		foreach ( $new_active_modules as $module ) {
565
			/**
566
			 * Fires when a specific module is activated.
567
			 *
568
			 * @since 1.9.0
569
			 *
570
			 * @param string $module Module slug.
571
			 * @param boolean $success whether the module was activated. @since 4.2
572
			 */
573
			do_action( 'jetpack_activate_module', $module, $success );
574
			/**
575
			 * Fires when a module is activated.
576
			 * The dynamic part of the filter, $module, is the module slug.
577
			 *
578
			 * @since 1.9.0
579
			 *
580
			 * @param string $module Module slug.
581
			 */
582
			do_action( "jetpack_activate_module_$module", $module );
583
		}
584
585
		foreach ( $new_inactive_modules as $module ) {
586
			/**
587
			 * Fired after a module has been deactivated.
588
			 *
589
			 * @since 4.2.0
590
			 *
591
			 * @param string $module Module slug.
592
			 * @param boolean $success whether the module was deactivated.
593
			 */
594
			do_action( 'jetpack_deactivate_module', $module, $success );
595
			/**
596
			 * Fires when a module is deactivated.
597
			 * The dynamic part of the filter, $module, is the module slug.
598
			 *
599
			 * @since 1.9.0
600
			 *
601
			 * @param string $module Module slug.
602
			 */
603
			do_action( "jetpack_deactivate_module_$module", $module );
604
		}
605
606
		return $success;
607
	}
608
609
	static function delete_active_modules() {
610
		self::update_active_modules( array() );
611
	}
612
613
	/**
614
	 * Adds a hook to plugins_loaded at a priority that's currently the earliest
615
	 * available.
616
	 */
617
	public function add_configure_hook() {
618
		global $wp_filter;
619
620
		$current_priority = has_filter( 'plugins_loaded', array( $this, 'configure' ) );
621
		if ( false !== $current_priority ) {
622
			remove_action( 'plugins_loaded', array( $this, 'configure' ), $current_priority );
623
		}
624
625
		$taken_priorities = array_map( 'intval', array_keys( $wp_filter['plugins_loaded']->callbacks ) );
626
		sort( $taken_priorities );
627
628
		$first_priority = array_shift( $taken_priorities );
629
630
		if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
631
			$new_priority = - PHP_INT_MAX;
632
		} else {
633
			$new_priority = $first_priority - 1;
634
		}
635
636
		add_action( 'plugins_loaded', array( $this, 'configure' ), $new_priority );
637
	}
638
639
	/**
640
	 * Constructor.  Initializes WordPress hooks
641
	 */
642
	private function __construct() {
643
		/*
644
		 * Check for and alert any deprecated hooks
645
		 */
646
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
647
648
		// Note how this runs at an earlier plugin_loaded hook intentionally to accomodate for other plugins.
649
		add_action( 'plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
650
		add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
651
		add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
652
		add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
653
654
		add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
655
656
		add_filter(
657
			'jetpack_signature_check_token',
658
			array( __CLASS__, 'verify_onboarding_token' ),
659
			10,
660
			3
661
		);
662
663
		/**
664
		 * Prepare Gutenberg Editor functionality
665
		 */
666
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
667
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
668
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
669
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_extended_blocks' ), 9 );
670
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
671
672
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
673
674
		// Unlink user before deleting the user from WP.com.
675
		add_action( 'deleted_user', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
676
		add_action( 'remove_user_from_blog', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
677
678
		add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
679
680
		add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
681
		add_action( 'login_init', array( $this, 'login_init' ) );
682
683
		// Set up the REST authentication hooks.
684
		Connection_Rest_Authentication::init();
685
686
		add_action( 'admin_init', array( $this, 'admin_init' ) );
687
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
688
689
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
690
691
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
692
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
693
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
694
695
		// returns HTTPS support status
696
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
697
698
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
699
700
		add_action( 'wp_ajax_jetpack_wizard_banner', array( 'Jetpack_Wizard_Banner', 'ajax_callback' ) );
701
702
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
703
704
		/**
705
		 * These actions run checks to load additional files.
706
		 * They check for external files or plugins, so they need to run as late as possible.
707
		 */
708
		add_action( 'wp_head', array( $this, 'check_open_graph' ), 1 );
709
		add_action( 'web_stories_story_head', array( $this, 'check_open_graph' ), 1 );
710
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ), 999 );
711
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
712
713
		add_filter( 'plugins_url', array( 'Jetpack', 'maybe_min_asset' ), 1, 3 );
714
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2 );
715
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
716
717
		add_filter( 'profile_update', array( 'Jetpack', 'user_meta_cleanup' ) );
718
719
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
720
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
721
722
		// A filter to control all just in time messages
723
		add_filter( 'jetpack_just_in_time_msgs', '__return_true', 9 );
724
725
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9 );
726
727
		/*
728
		 * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
729
		 * We should make sure to only do this for front end links.
730
		 */
731
		if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
732
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
733
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
734
735
			/*
736
			 * We'll shortcircuit wp_notify_postauthor and wp_notify_moderator pluggable functions
737
			 * so they point moderation links on emails to Calypso.
738
			 */
739
			jetpack_require_lib( 'functions.wp-notify' );
740
			add_filter( 'comment_notification_recipients', 'jetpack_notify_postauthor', 1, 2 );
741
			add_filter( 'notify_moderator', 'jetpack_notify_moderator', 1, 2 );
742
		}
743
744
		add_action(
745
			'plugins_loaded',
746
			function () {
747
				if ( User_Agent_Info::is_mobile_app() ) {
748
					add_filter( 'get_edit_post_link', '__return_empty_string' );
749
				}
750
			}
751
		);
752
753
		// Update the site's Jetpack plan and products from API on heartbeats.
754
		add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
755
756
		/**
757
		 * This is the hack to concatenate all css files into one.
758
		 * For description and reasoning see the implode_frontend_css method.
759
		 *
760
		 * Super late priority so we catch all the registered styles.
761
		 */
762
		if ( ! is_admin() ) {
763
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
764
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
765
		}
766
767
		/**
768
		 * These are sync actions that we need to keep track of for jitms
769
		 */
770
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
771
772
		// Actually push the stats on shutdown.
773
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
774
			add_action( 'shutdown', array( $this, 'push_stats' ) );
775
		}
776
777
		// Actions for Manager::authorize().
778
		add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
779
		add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
780
		add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
781
782
		// Filters for the Manager::get_token() urls and request body.
783
		add_filter( 'jetpack_token_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
784
		add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
785
		add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
786
787
		// Actions for successful reconnect.
788
		add_action( 'jetpack_reconnection_completed', array( $this, 'reconnection_completed' ) );
789
790
		// Actions for licensing.
791
		Licensing::instance()->initialize();
792
793
		// Make resources use static domain when possible.
794
		add_filter( 'jetpack_static_url', array( 'Automattic\\Jetpack\\Assets', 'staticize_subdomain' ) );
795
	}
796
797
	/**
798
	 * Before everything else starts getting initalized, we need to initialize Jetpack using the
799
	 * Config object.
800
	 */
801
	public function configure() {
802
		$config = new Config();
803
804
		foreach (
805
			array(
806
				'sync',
807
			)
808
			as $feature
809
		) {
810
			$config->ensure( $feature );
811
		}
812
813
		$config->ensure(
814
			'connection',
815
			array(
816
				'slug' => 'jetpack',
817
				'name' => 'Jetpack',
818
			)
819
		);
820
821
		if ( is_admin() ) {
822
			$config->ensure( 'jitm' );
823
		}
824
825
		if ( ! $this->connection_manager ) {
826
			$this->connection_manager = new Connection_Manager( 'jetpack' );
827
			Automattic\Jetpack\ConnectionUI\Admin::init();
828
		}
829
830
		/*
831
		 * Load things that should only be in Network Admin.
832
		 *
833
		 * For now blow away everything else until a more full
834
		 * understanding of what is needed at the network level is
835
		 * available
836
		 */
837
		if ( is_multisite() ) {
838
			$network = Jetpack_Network::init();
839
			$network->set_connection( $this->connection_manager );
840
		}
841
842
		if ( $this->connection_manager->is_active() ) {
843
			add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
844
845
			Jetpack_Heartbeat::init();
846
			if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
847
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
848
				Jetpack_Search_Performance_Logger::init();
849
			}
850
		}
851
852
		// Initialize remote file upload request handlers.
853
		$this->add_remote_request_handlers();
854
855
		/*
856
		 * Enable enhanced handling of previewing sites in Calypso
857
		 */
858
		if ( self::is_active() ) {
859
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
860
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
861
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
862
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
863
		}
864
865
		if ( ( new Tracking( $this->connection_manager ) )->should_enable_tracking( new Terms_Of_Service(), new Status() ) ) {
0 ignored issues
show
$this->connection_manager is of type object<Automattic\Jetpack\Connection\Manager>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
new \Automattic\Jetpack\Terms_Of_Service() is of type object<Automattic\Jetpack\Terms_Of_Service>, but the function expects a object<Automattic\Jetpac...tpack\Terms_Of_Service>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
new \Automattic\Jetpack\Status() is of type object<Automattic\Jetpack\Status>, but the function expects a object<Automattic\Jetpac...omattic\Jetpack\Status>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
866
			add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
867
		} else {
868
			/**
869
			 * Initialize tracking right after the user agrees to the terms of service.
870
			 */
871
			add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
872
		}
873
	}
874
875
	/**
876
	 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
877
	 * initialization code.
878
	 *
879
	 * @action plugins_loaded
880
	 */
881
	public function late_initialization() {
882
		add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
883
884
		Partner::init();
885
886
		/**
887
		 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
888
		 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
889
		 *
890
		 * @since 8.1.0
891
		 *
892
		 * @param Jetpack $jetpack the main plugin class object.
893
		 */
894
		do_action( 'jetpack_loaded', $this );
895
896
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
897
	}
898
899
	/**
900
	 * Sets up the XMLRPC request handlers.
901
	 *
902
	 * @deprecated since 7.7.0
903
	 * @see Automattic\Jetpack\Connection\Manager::setup_xmlrpc_handlers()
904
	 *
905
	 * @param array                 $request_params Incoming request parameters.
906
	 * @param Boolean               $is_active      Whether the connection is currently active.
907
	 * @param Boolean               $is_signed      Whether the signature check has been successful.
908
	 * @param Jetpack_XMLRPC_Server $xmlrpc_server  (optional) An instance of the server to use instead of instantiating a new one.
0 ignored issues
show
Should the type for parameter $xmlrpc_server not be null|Jetpack_XMLRPC_Server?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
909
	 */
910 View Code Duplication
	public function setup_xmlrpc_handlers(
911
		$request_params,
912
		$is_active,
913
		$is_signed,
914
		Jetpack_XMLRPC_Server $xmlrpc_server = null
915
	) {
916
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::setup_xmlrpc_handlers' );
917
918
		if ( ! $this->connection_manager ) {
919
			$this->connection_manager = new Connection_Manager();
920
		}
921
922
		return $this->connection_manager->setup_xmlrpc_handlers(
923
			$request_params,
924
			$is_active,
925
			$is_signed,
926
			$xmlrpc_server
927
		);
928
	}
929
930
	/**
931
	 * Initialize REST API registration connector.
932
	 *
933
	 * @deprecated since 7.7.0
934
	 * @see Automattic\Jetpack\Connection\Manager::initialize_rest_api_registration_connector()
935
	 */
936 View Code Duplication
	public function initialize_rest_api_registration_connector() {
937
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::initialize_rest_api_registration_connector' );
938
939
		if ( ! $this->connection_manager ) {
940
			$this->connection_manager = new Connection_Manager();
941
		}
942
943
		$this->connection_manager->initialize_rest_api_registration_connector();
944
	}
945
946
	/**
947
	 * This is ported over from the manage module, which has been deprecated and baked in here.
948
	 *
949
	 * @param $domains
950
	 */
951
	function add_wpcom_to_allowed_redirect_hosts( $domains ) {
952
		add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) );
953
	}
954
955
	/**
956
	 * Return $domains, with 'wordpress.com' appended.
957
	 * This is ported over from the manage module, which has been deprecated and baked in here.
958
	 *
959
	 * @param $domains
960
	 * @return array
961
	 */
962
	function allow_wpcom_domain( $domains ) {
963
		if ( empty( $domains ) ) {
964
			$domains = array();
965
		}
966
		$domains[] = 'wordpress.com';
967
		return array_unique( $domains );
968
	}
969
970
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
971
		$post = get_post( $post_id );
972
973
		if ( empty( $post ) ) {
974
			return $default_url;
975
		}
976
977
		$post_type = $post->post_type;
978
979
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
980
		// https://en.support.wordpress.com/custom-post-types/
981
		$allowed_post_types = array(
982
			'post',
983
			'page',
984
			'jetpack-portfolio',
985
			'jetpack-testimonial',
986
		);
987
988
		if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
989
			return $default_url;
990
		}
991
992
		return Redirect::get_url(
993
			'calypso-edit-' . $post_type,
994
			array(
995
				'path' => $post_id,
996
			)
997
		);
998
	}
999
1000
	function point_edit_comment_links_to_calypso( $url ) {
1001
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
1002
		wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
0 ignored issues
show
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1003
1004
		return Redirect::get_url(
1005
			'calypso-edit-comment',
1006
			array(
1007
				'path' => $query_args['amp;c'],
1008
			)
1009
		);
1010
1011
	}
1012
1013
	function jetpack_track_last_sync_callback( $params ) {
1014
		/**
1015
		 * Filter to turn off jitm caching
1016
		 *
1017
		 * @since 5.4.0
1018
		 *
1019
		 * @param bool false Whether to cache just in time messages
1020
		 */
1021
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
1022
			return $params;
1023
		}
1024
1025
		if ( is_array( $params ) && isset( $params[0] ) ) {
1026
			$option = $params[0];
1027
			if ( 'active_plugins' === $option ) {
1028
				// use the cache if we can, but not terribly important if it gets evicted
1029
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
1030
			}
1031
		}
1032
1033
		return $params;
1034
	}
1035
1036
	function jetpack_connection_banner_callback() {
1037
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1038
1039
		// Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1040
		if (
1041
			isset( $_REQUEST['dismissBanner'] ) &&
1042
			! Jetpack_Connection_Banner::force_display()
1043
		) {
1044
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1045
			wp_send_json_success();
1046
		}
1047
1048
		wp_die();
1049
	}
1050
1051
	/**
1052
	 * Removes all XML-RPC methods that are not `jetpack.*`.
1053
	 * Only used in our alternate XML-RPC endpoint, where we want to
1054
	 * ensure that Core and other plugins' methods are not exposed.
1055
	 *
1056
	 * @deprecated since 7.7.0
1057
	 * @see Automattic\Jetpack\Connection\Manager::remove_non_jetpack_xmlrpc_methods()
1058
	 *
1059
	 * @param array $methods A list of registered WordPress XMLRPC methods.
1060
	 * @return array Filtered $methods
1061
	 */
1062 View Code Duplication
	public function remove_non_jetpack_xmlrpc_methods( $methods ) {
1063
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::remove_non_jetpack_xmlrpc_methods' );
1064
1065
		if ( ! $this->connection_manager ) {
1066
			$this->connection_manager = new Connection_Manager();
1067
		}
1068
1069
		return $this->connection_manager->remove_non_jetpack_xmlrpc_methods( $methods );
1070
	}
1071
1072
	/**
1073
	 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
1074
	 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
1075
	 * security/firewall policies, we provide our own alternate XML RPC API endpoint
1076
	 * which is accessible via a different URI. Most of the below is copied directly
1077
	 * from /xmlrpc.php so that we're replicating it as closely as possible.
1078
	 *
1079
	 * @deprecated since 7.7.0
1080
	 * @see Automattic\Jetpack\Connection\Manager::alternate_xmlrpc()
1081
	 */
1082 View Code Duplication
	public function alternate_xmlrpc() {
1083
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::alternate_xmlrpc' );
1084
1085
		if ( ! $this->connection_manager ) {
1086
			$this->connection_manager = new Connection_Manager();
1087
		}
1088
1089
		$this->connection_manager->alternate_xmlrpc();
1090
	}
1091
1092
	/**
1093
	 * The callback for the JITM ajax requests.
1094
	 *
1095
	 * @deprecated since 7.9.0
1096
	 */
1097
	function jetpack_jitm_ajax_callback() {
1098
		_deprecated_function( __METHOD__, 'jetpack-7.9' );
1099
	}
1100
1101
	/**
1102
	 * If there are any stats that need to be pushed, but haven't been, push them now.
1103
	 */
1104
	function push_stats() {
1105
		if ( ! empty( $this->stats ) ) {
1106
			$this->do_stats( 'server_side' );
1107
		}
1108
	}
1109
1110
	/**
1111
	 * Sets the Jetpack custom capabilities.
1112
	 *
1113
	 * @param string[] $caps    Array of the user's capabilities.
1114
	 * @param string   $cap     Capability name.
1115
	 * @param int      $user_id The user ID.
1116
	 * @param array    $args    Adds the context to the cap. Typically the object ID.
1117
	 */
1118
	public function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
1119
		$is_offline_mode = ( new Status() )->is_offline_mode();
1120
		switch ( $cap ) {
1121
			case 'jetpack_manage_modules':
1122
			case 'jetpack_activate_modules':
1123
			case 'jetpack_deactivate_modules':
1124
				$caps = array( 'manage_options' );
1125
				break;
1126
			case 'jetpack_configure_modules':
1127
				$caps = array( 'manage_options' );
1128
				break;
1129
			case 'jetpack_manage_autoupdates':
1130
				$caps = array(
1131
					'manage_options',
1132
					'update_plugins',
1133
				);
1134
				break;
1135
			case 'jetpack_network_admin_page':
1136
			case 'jetpack_network_settings_page':
1137
				$caps = array( 'manage_network_plugins' );
1138
				break;
1139
			case 'jetpack_network_sites_page':
1140
				$caps = array( 'manage_sites' );
1141
				break;
1142
			case 'jetpack_admin_page':
1143
				if ( $is_offline_mode ) {
1144
					$caps = array( 'manage_options' );
1145
					break;
1146
				} else {
1147
					$caps = array( 'read' );
1148
				}
1149
				break;
1150
		}
1151
		return $caps;
1152
	}
1153
1154
	/**
1155
	 * Require a Jetpack authentication.
1156
	 *
1157
	 * @deprecated since 7.7.0
1158
	 * @see Automattic\Jetpack\Connection\Manager::require_jetpack_authentication()
1159
	 */
1160 View Code Duplication
	public function require_jetpack_authentication() {
1161
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::require_jetpack_authentication' );
1162
1163
		if ( ! $this->connection_manager ) {
1164
			$this->connection_manager = new Connection_Manager();
1165
		}
1166
1167
		$this->connection_manager->require_jetpack_authentication();
1168
	}
1169
1170
	/**
1171
	 * Register assets for use in various modules and the Jetpack admin page.
1172
	 *
1173
	 * @uses wp_script_is, wp_register_script, plugins_url
1174
	 * @action wp_loaded
1175
	 * @return null
1176
	 */
1177
	public function register_assets() {
1178 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1179
			wp_register_script(
1180
				'jetpack-gallery-settings',
1181
				Assets::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1182
				array( 'media-views' ),
1183
				'20121225'
1184
			);
1185
		}
1186
1187
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1188
			wp_register_script(
1189
				'jetpack-twitter-timeline',
1190
				Assets::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1191
				array( 'jquery' ),
1192
				'4.0.0',
1193
				true
1194
			);
1195
		}
1196
1197
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1198
			wp_register_script(
1199
				'jetpack-facebook-embed',
1200
				Assets::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1201
				array(),
1202
				null,
1203
				true
1204
			);
1205
1206
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1207
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1208
			if ( ! is_numeric( $fb_app_id ) ) {
1209
				$fb_app_id = '';
1210
			}
1211
			wp_localize_script(
1212
				'jetpack-facebook-embed',
1213
				'jpfbembed',
1214
				array(
1215
					'appid'  => $fb_app_id,
1216
					'locale' => $this->get_locale(),
1217
				)
1218
			);
1219
		}
1220
1221
		/**
1222
		 * As jetpack_register_genericons is by default fired off a hook,
1223
		 * the hook may have already fired by this point.
1224
		 * So, let's just trigger it manually.
1225
		 */
1226
		require_once JETPACK__PLUGIN_DIR . '_inc/genericons.php';
1227
		jetpack_register_genericons();
1228
1229
		/**
1230
		 * Register the social logos
1231
		 */
1232
		require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1233
		jetpack_register_social_logos();
1234
1235 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1236
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1237
		}
1238
	}
1239
1240
	/**
1241
	 * Guess locale from language code.
1242
	 *
1243
	 * @param string $lang Language code.
1244
	 * @return string|bool
1245
	 */
1246 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1247
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1248
			return 'en_US';
1249
		}
1250
1251
		if ( ! class_exists( 'GP_Locales' ) ) {
1252
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1253
				return false;
1254
			}
1255
1256
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1257
		}
1258
1259
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1260
			// WP.com: get_locale() returns 'it'
1261
			$locale = GP_Locales::by_slug( $lang );
1262
		} else {
1263
			// Jetpack: get_locale() returns 'it_IT';
1264
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1265
		}
1266
1267
		if ( ! $locale ) {
1268
			return false;
1269
		}
1270
1271
		if ( empty( $locale->facebook_locale ) ) {
1272
			if ( empty( $locale->wp_locale ) ) {
1273
				return false;
1274
			} else {
1275
				// Facebook SDK is smart enough to fall back to en_US if a
1276
				// locale isn't supported. Since supported Facebook locales
1277
				// can fall out of sync, we'll attempt to use the known
1278
				// wp_locale value and rely on said fallback.
1279
				return $locale->wp_locale;
1280
			}
1281
		}
1282
1283
		return $locale->facebook_locale;
1284
	}
1285
1286
	/**
1287
	 * Get the locale.
1288
	 *
1289
	 * @return string|bool
1290
	 */
1291
	function get_locale() {
1292
		$locale = $this->guess_locale_from_lang( get_locale() );
1293
1294
		if ( ! $locale ) {
1295
			$locale = 'en_US';
1296
		}
1297
1298
		return $locale;
1299
	}
1300
1301
	/**
1302
	 * Return the network_site_url so that .com knows what network this site is a part of.
1303
	 *
1304
	 * @param  bool $option
1305
	 * @return string
1306
	 */
1307
	public function jetpack_main_network_site_option( $option ) {
1308
		return network_site_url();
1309
	}
1310
	/**
1311
	 * Network Name.
1312
	 */
1313
	static function network_name( $option = null ) {
1314
		global $current_site;
1315
		return $current_site->site_name;
1316
	}
1317
	/**
1318
	 * Does the network allow new user and site registrations.
1319
	 *
1320
	 * @return string
1321
	 */
1322
	static function network_allow_new_registrations( $option = null ) {
1323
		return ( in_array( get_site_option( 'registration' ), array( 'none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration' ) : 'none' );
1324
	}
1325
	/**
1326
	 * Does the network allow admins to add new users.
1327
	 *
1328
	 * @return boolian
1329
	 */
1330
	static function network_add_new_users( $option = null ) {
1331
		return (bool) get_site_option( 'add_new_users' );
1332
	}
1333
	/**
1334
	 * File upload psace left per site in MB.
1335
	 *  -1 means NO LIMIT.
1336
	 *
1337
	 * @return number
1338
	 */
1339
	static function network_site_upload_space( $option = null ) {
1340
		// value in MB
1341
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1342
	}
1343
1344
	/**
1345
	 * Network allowed file types.
1346
	 *
1347
	 * @return string
1348
	 */
1349
	static function network_upload_file_types( $option = null ) {
1350
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1351
	}
1352
1353
	/**
1354
	 * Maximum file upload size set by the network.
1355
	 *
1356
	 * @return number
1357
	 */
1358
	static function network_max_upload_file_size( $option = null ) {
1359
		// value in KB
1360
		return get_site_option( 'fileupload_maxk', 300 );
1361
	}
1362
1363
	/**
1364
	 * Lets us know if a site allows admins to manage the network.
1365
	 *
1366
	 * @return array
1367
	 */
1368
	static function network_enable_administration_menus( $option = null ) {
1369
		return get_site_option( 'menu_items' );
1370
	}
1371
1372
	/**
1373
	 * If a user has been promoted to or demoted from admin, we need to clear the
1374
	 * jetpack_other_linked_admins transient.
1375
	 *
1376
	 * @since 4.3.2
1377
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1378
	 *
1379
	 * @param int    $user_id   The user ID whose role changed.
1380
	 * @param string $role      The new role.
1381
	 * @param array  $old_roles An array of the user's previous roles.
0 ignored issues
show
Should the type for parameter $old_roles not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1382
	 */
1383
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1384
		if ( 'administrator' == $role
1385
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1386
			|| is_null( $old_roles )
1387
		) {
1388
			delete_transient( 'jetpack_other_linked_admins' );
1389
		}
1390
	}
1391
1392
	/**
1393
	 * Checks to see if there are any other users available to become primary
1394
	 * Users must both:
1395
	 * - Be linked to wpcom
1396
	 * - Be an admin
1397
	 *
1398
	 * @return mixed False if no other users are linked, Int if there are.
1399
	 */
1400
	static function get_other_linked_admins() {
1401
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1402
1403
		if ( false === $other_linked_users ) {
1404
			$admins = get_users( array( 'role' => 'administrator' ) );
1405
			if ( count( $admins ) > 1 ) {
1406
				$available = array();
1407
				foreach ( $admins as $admin ) {
1408
					if ( self::is_user_connected( $admin->ID ) ) {
1409
						$available[] = $admin->ID;
1410
					}
1411
				}
1412
1413
				$count_connected_admins = count( $available );
1414
				if ( count( $available ) > 1 ) {
1415
					$other_linked_users = $count_connected_admins;
1416
				} else {
1417
					$other_linked_users = 0;
1418
				}
1419
			} else {
1420
				$other_linked_users = 0;
1421
			}
1422
1423
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1424
		}
1425
1426
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1427
	}
1428
1429
	/**
1430
	 * Return whether we are dealing with a multi network setup or not.
1431
	 * The reason we are type casting this is because we want to avoid the situation where
1432
	 * the result is false since when is_main_network_option return false it cases
1433
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1434
	 * database which could be set to anything as opposed to what this function returns.
1435
	 *
1436
	 * @param  bool $option
1437
	 *
1438
	 * @return boolean
1439
	 */
1440
	public function is_main_network_option( $option ) {
1441
		// return '1' or ''
1442
		return (string) (bool) self::is_multi_network();
1443
	}
1444
1445
	/**
1446
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1447
	 *
1448
	 * @param  string $option
1449
	 * @return boolean
1450
	 */
1451
	public function is_multisite( $option ) {
1452
		return (string) (bool) is_multisite();
1453
	}
1454
1455
	/**
1456
	 * Implemented since there is no core is multi network function
1457
	 * Right now there is no way to tell if we which network is the dominant network on the system
1458
	 *
1459
	 * @since  3.3
1460
	 * @return boolean
1461
	 */
1462 View Code Duplication
	public static function is_multi_network() {
1463
		global  $wpdb;
1464
1465
		// if we don't have a multi site setup no need to do any more
1466
		if ( ! is_multisite() ) {
1467
			return false;
1468
		}
1469
1470
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1471
		if ( $num_sites > 1 ) {
1472
			return true;
1473
		} else {
1474
			return false;
1475
		}
1476
	}
1477
1478
	/**
1479
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1480
	 *
1481
	 * @return null
1482
	 */
1483
	function update_jetpack_main_network_site_option() {
1484
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1485
	}
1486
	/**
1487
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1488
	 */
1489
	function update_jetpack_network_settings() {
1490
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1491
		// Only sync this info for the main network site.
1492
	}
1493
1494
	/**
1495
	 * Get back if the current site is single user site.
1496
	 *
1497
	 * @return bool
1498
	 */
1499 View Code Duplication
	public static function is_single_user_site() {
1500
		global $wpdb;
1501
1502
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1503
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1504
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1505
		}
1506
		return 1 === (int) $some_users;
1507
	}
1508
1509
	/**
1510
	 * Returns true if the site has file write access false otherwise.
1511
	 *
1512
	 * @return string ( '1' | '0' )
1513
	 **/
1514
	public static function file_system_write_access() {
1515
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1516
			require_once ABSPATH . 'wp-admin/includes/file.php';
1517
		}
1518
1519
		require_once ABSPATH . 'wp-admin/includes/template.php';
1520
1521
		$filesystem_method = get_filesystem_method();
1522
		if ( $filesystem_method === 'direct' ) {
1523
			return 1;
1524
		}
1525
1526
		ob_start();
1527
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1528
		ob_end_clean();
1529
		if ( $filesystem_credentials_are_stored ) {
1530
			return 1;
1531
		}
1532
		return 0;
1533
	}
1534
1535
	/**
1536
	 * Finds out if a site is using a version control system.
1537
	 *
1538
	 * @return string ( '1' | '0' )
1539
	 **/
1540
	public static function is_version_controlled() {
1541
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' );
1542
		return (string) (int) Functions::is_version_controlled();
1543
	}
1544
1545
	/**
1546
	 * Determines whether the current theme supports featured images or not.
1547
	 *
1548
	 * @return string ( '1' | '0' )
1549
	 */
1550
	public static function featured_images_enabled() {
1551
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1552
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1553
	}
1554
1555
	/**
1556
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1557
	 *
1558
	 * @deprecated 4.7 use get_avatar_url instead.
1559
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1560
	 * @param int               $size Size of the avatar image
1561
	 * @param string            $default URL to a default image to use if no avatar is available
1562
	 * @param bool              $force_display Whether to force it to return an avatar even if show_avatars is disabled
1563
	 *
1564
	 * @return array
1565
	 */
1566
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1567
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1568
		return get_avatar_url(
1569
			$id_or_email,
1570
			array(
1571
				'size'          => $size,
1572
				'default'       => $default,
1573
				'force_default' => $force_display,
1574
			)
1575
		);
1576
	}
1577
// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled
1578
	/**
1579
	 * jetpack_updates is saved in the following schema:
1580
	 *
1581
	 * array (
1582
	 *      'plugins'                       => (int) Number of plugin updates available.
1583
	 *      'themes'                        => (int) Number of theme updates available.
1584
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1585
	 *      'translations'                  => (int) Number of translation updates available.
1586
	 *      'total'                         => (int) Total of all available updates.
1587
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1588
	 * )
1589
	 *
1590
	 * @return array
1591
	 */
1592
	public static function get_updates() {
1593
		$update_data = wp_get_update_data();
1594
1595
		// Stores the individual update counts as well as the total count.
1596
		if ( isset( $update_data['counts'] ) ) {
1597
			$updates = $update_data['counts'];
1598
		}
1599
1600
		// If we need to update WordPress core, let's find the latest version number.
1601 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1602
			$cur = get_preferred_from_update_core();
1603
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1604
				$updates['wp_update_version'] = $cur->current;
1605
			}
1606
		}
1607
		return isset( $updates ) ? $updates : array();
1608
	}
1609
	// phpcs:enable
1610
1611
	public static function get_update_details() {
1612
		$update_details = array(
1613
			'update_core'    => get_site_transient( 'update_core' ),
1614
			'update_plugins' => get_site_transient( 'update_plugins' ),
1615
			'update_themes'  => get_site_transient( 'update_themes' ),
1616
		);
1617
		return $update_details;
1618
	}
1619
1620
	public static function refresh_update_data() {
1621
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1622
1623
	}
1624
1625
	public static function refresh_theme_data() {
1626
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1627
	}
1628
1629
	/**
1630
	 * Is Jetpack active?
1631
	 * The method only checks if there's an existing token for the master user. It doesn't validate the token.
1632
	 *
1633
	 * @return bool
1634
	 */
1635
	public static function is_active() {
1636
		return self::connection()->is_active();
1637
	}
1638
1639
	/**
1640
	 * Make an API call to WordPress.com for plan status
1641
	 *
1642
	 * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom.
1643
	 *
1644
	 * @return bool True if plan is updated, false if no update
1645
	 */
1646
	public static function refresh_active_plan_from_wpcom() {
1647
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' );
1648
		return Jetpack_Plan::refresh_from_wpcom();
1649
	}
1650
1651
	/**
1652
	 * Get the plan that this Jetpack site is currently using
1653
	 *
1654
	 * @deprecated 7.2.0 Use Jetpack_Plan::get.
1655
	 * @return array Active Jetpack plan details.
1656
	 */
1657
	public static function get_active_plan() {
1658
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' );
1659
		return Jetpack_Plan::get();
1660
	}
1661
1662
	/**
1663
	 * Determine whether the active plan supports a particular feature
1664
	 *
1665
	 * @deprecated 7.2.0 Use Jetpack_Plan::supports.
1666
	 * @return bool True if plan supports feature, false if not.
1667
	 */
1668
	public static function active_plan_supports( $feature ) {
1669
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' );
1670
		return Jetpack_Plan::supports( $feature );
1671
	}
1672
1673
	/**
1674
	 * Deprecated: Is Jetpack in development (offline) mode?
1675
	 *
1676
	 * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1677
	 * and themes still use it, and we do not want to flood them with notices.
1678
	 *
1679
	 * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1680
	 *
1681
	 * @deprecated since 8.0.
1682
	 */
1683
	public static function is_development_mode() {
1684
		return ( new Status() )->is_offline_mode();
1685
	}
1686
1687
	/**
1688
	 * Whether the site is currently onboarding or not.
1689
	 * A site is considered as being onboarded if it currently has an onboarding token.
1690
	 *
1691
	 * @since 5.8
1692
	 *
1693
	 * @access public
1694
	 * @static
1695
	 *
1696
	 * @return bool True if the site is currently onboarding, false otherwise
1697
	 */
1698
	public static function is_onboarding() {
1699
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1700
	}
1701
1702
	/**
1703
	 * Determines reason for Jetpack offline mode.
1704
	 */
1705
	public static function development_mode_trigger_text() {
1706
		$status = new Status();
1707
1708
		if ( ! $status->is_offline_mode() ) {
1709
			return __( 'Jetpack is not in Offline Mode.', 'jetpack' );
1710
		}
1711
1712
		if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1713
			$notice = __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1714
		} elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1715
			$notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1716
		} elseif ( $status->is_local_site() ) {
1717
			$notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1718
			/** This filter is documented in packages/status/src/class-status.php */
1719
		} elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1720
			$notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1721
		} else {
1722
			$notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1723
		}
1724
1725
		return $notice;
1726
1727
	}
1728
	/**
1729
	 * Get Jetpack offline mode notice text and notice class.
1730
	 *
1731
	 * Mirrors the checks made in Automattic\Jetpack\Status->is_offline_mode
1732
	 */
1733
	public static function show_development_mode_notice() {
1734 View Code Duplication
		if ( ( new Status() )->is_offline_mode() ) {
1735
			$notice = sprintf(
1736
				/* translators: %s is a URL */
1737
				__( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1738
				Redirect::get_url( 'jetpack-support-development-mode' )
1739
			);
1740
1741
			$notice .= ' ' . self::development_mode_trigger_text();
1742
1743
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1744
		}
1745
1746
		// Throw up a notice if using a development version and as for feedback.
1747
		if ( self::is_development_version() ) {
1748
			/* translators: %s is a URL */
1749
			$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' ) );
1750
1751
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1752
		}
1753
		// Throw up a notice if using staging mode
1754 View Code Duplication
		if ( ( new Status() )->is_staging_site() ) {
1755
			/* translators: %s is a URL */
1756
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1757
1758
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1759
		}
1760
	}
1761
1762
	/**
1763
	 * Whether Jetpack's version maps to a public release, or a development version.
1764
	 */
1765
	public static function is_development_version() {
1766
		/**
1767
		 * Allows filtering whether this is a development version of Jetpack.
1768
		 *
1769
		 * This filter is especially useful for tests.
1770
		 *
1771
		 * @since 4.3.0
1772
		 *
1773
		 * @param bool $development_version Is this a develoment version of Jetpack?
1774
		 */
1775
		return (bool) apply_filters(
1776
			'jetpack_development_version',
1777
			! preg_match( '/^\d+(\.\d+)+$/', Constants::get_constant( 'JETPACK__VERSION' ) )
1778
		);
1779
	}
1780
1781
	/**
1782
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1783
	 */
1784
	public static function is_user_connected( $user_id = false ) {
1785
		return self::connection()->is_user_connected( $user_id );
0 ignored issues
show
$user_id is of type boolean, but the function expects a false|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1786
	}
1787
1788
	/**
1789
	 * Get the wpcom user data of the current|specified connected user.
1790
	 */
1791 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1792
		// TODO: remove in favor of Connection_Manager->get_connected_user_data
1793
		if ( ! $user_id ) {
1794
			$user_id = get_current_user_id();
1795
		}
1796
1797
		$transient_key = "jetpack_connected_user_data_$user_id";
1798
1799
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1800
			return $cached_user_data;
1801
		}
1802
1803
		$xml = new Jetpack_IXR_Client(
1804
			array(
1805
				'user_id' => $user_id,
1806
			)
1807
		);
1808
		$xml->query( 'wpcom.getUser' );
1809
		if ( ! $xml->isError() ) {
1810
			$user_data = $xml->getResponse();
1811
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1812
			return $user_data;
1813
		}
1814
1815
		return false;
1816
	}
1817
1818
	/**
1819
	 * Get the wpcom email of the current|specified connected user.
1820
	 */
1821
	public static function get_connected_user_email( $user_id = null ) {
1822
		if ( ! $user_id ) {
1823
			$user_id = get_current_user_id();
1824
		}
1825
1826
		$xml = new Jetpack_IXR_Client(
1827
			array(
1828
				'user_id' => $user_id,
1829
			)
1830
		);
1831
		$xml->query( 'wpcom.getUserEmail' );
1832
		if ( ! $xml->isError() ) {
1833
			return $xml->getResponse();
1834
		}
1835
		return false;
1836
	}
1837
1838
	/**
1839
	 * Get the wpcom email of the master user.
1840
	 */
1841
	public static function get_master_user_email() {
1842
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1843
		if ( $master_user_id ) {
1844
			return self::get_connected_user_email( $master_user_id );
1845
		}
1846
		return '';
1847
	}
1848
1849
	/**
1850
	 * Whether the current user is the connection owner.
1851
	 *
1852
	 * @deprecated since 7.7
1853
	 *
1854
	 * @return bool Whether the current user is the connection owner.
1855
	 */
1856
	public function current_user_is_connection_owner() {
1857
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::is_connection_owner' );
1858
		return self::connection()->is_connection_owner();
1859
	}
1860
1861
	/**
1862
	 * Gets current user IP address.
1863
	 *
1864
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1865
	 *
1866
	 * @return string                  Current user IP address.
1867
	 */
1868
	public static function current_user_ip( $check_all_headers = false ) {
1869
		if ( $check_all_headers ) {
1870
			foreach ( array(
1871
				'HTTP_CF_CONNECTING_IP',
1872
				'HTTP_CLIENT_IP',
1873
				'HTTP_X_FORWARDED_FOR',
1874
				'HTTP_X_FORWARDED',
1875
				'HTTP_X_CLUSTER_CLIENT_IP',
1876
				'HTTP_FORWARDED_FOR',
1877
				'HTTP_FORWARDED',
1878
				'HTTP_VIA',
1879
			) as $key ) {
1880
				if ( ! empty( $_SERVER[ $key ] ) ) {
1881
					return $_SERVER[ $key ];
1882
				}
1883
			}
1884
		}
1885
1886
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1887
	}
1888
1889
	/**
1890
	 * Synchronize connected user role changes
1891
	 */
1892
	function user_role_change( $user_id ) {
1893
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' );
1894
		Users::user_role_change( $user_id );
1895
	}
1896
1897
	/**
1898
	 * Loads the currently active modules.
1899
	 */
1900
	public static function load_modules() {
1901
		$is_offline_mode = ( new Status() )->is_offline_mode();
1902
		if (
1903
			! self::is_active()
1904
			&& ! $is_offline_mode
1905
			&& ! self::is_onboarding()
1906
			&& (
1907
				! is_multisite()
1908
				|| ! get_site_option( 'jetpack_protect_active' )
1909
			)
1910
		) {
1911
			return;
1912
		}
1913
1914
		$version = Jetpack_Options::get_option( 'version' );
1915 View Code Duplication
		if ( ! $version ) {
1916
			$version = $old_version = JETPACK__VERSION . ':' . time();
1917
			/** This action is documented in class.jetpack.php */
1918
			do_action( 'updating_jetpack_version', $version, false );
1919
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1920
		}
1921
		list( $version ) = explode( ':', $version );
1922
1923
		$modules = array_filter( self::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1924
1925
		$modules_data = array();
1926
1927
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1928
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1929
			$updated_modules = array();
1930
			foreach ( $modules as $module ) {
1931
				$modules_data[ $module ] = self::get_module( $module );
1932
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1933
					continue;
1934
				}
1935
1936
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1937
					continue;
1938
				}
1939
1940
				$updated_modules[] = $module;
1941
			}
1942
1943
			$modules = array_diff( $modules, $updated_modules );
1944
		}
1945
1946
		foreach ( $modules as $index => $module ) {
1947
			// If we're in offline mode, disable modules requiring a connection.
1948
			if ( $is_offline_mode ) {
1949
				// Prime the pump if we need to
1950
				if ( empty( $modules_data[ $module ] ) ) {
1951
					$modules_data[ $module ] = self::get_module( $module );
1952
				}
1953
				// If the module requires a connection, but we're in local mode, don't include it.
1954
				if ( $modules_data[ $module ]['requires_connection'] ) {
1955
					continue;
1956
				}
1957
			}
1958
1959
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1960
				continue;
1961
			}
1962
1963
			if ( ! include_once self::get_module_path( $module ) ) {
1964
				unset( $modules[ $index ] );
1965
				self::update_active_modules( array_values( $modules ) );
1966
				continue;
1967
			}
1968
1969
			/**
1970
			 * Fires when a specific module is loaded.
1971
			 * The dynamic part of the hook, $module, is the module slug.
1972
			 *
1973
			 * @since 1.1.0
1974
			 */
1975
			do_action( 'jetpack_module_loaded_' . $module );
1976
		}
1977
1978
		/**
1979
		 * Fires when all the modules are loaded.
1980
		 *
1981
		 * @since 1.1.0
1982
		 */
1983
		do_action( 'jetpack_modules_loaded' );
1984
1985
		// 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.
1986
		require_once JETPACK__PLUGIN_DIR . 'modules/module-extras.php';
1987
	}
1988
1989
	/**
1990
	 * Check if Jetpack's REST API compat file should be included
1991
	 *
1992
	 * @action plugins_loaded
1993
	 * @return null
1994
	 */
1995
	public function check_rest_api_compat() {
1996
		/**
1997
		 * Filters the list of REST API compat files to be included.
1998
		 *
1999
		 * @since 2.2.5
2000
		 *
2001
		 * @param array $args Array of REST API compat files to include.
2002
		 */
2003
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
2004
2005
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include ) {
2006
			require_once $_jetpack_rest_api_compat_include;
2007
		}
2008
	}
2009
2010
	/**
2011
	 * Gets all plugins currently active in values, regardless of whether they're
2012
	 * traditionally activated or network activated.
2013
	 *
2014
	 * @todo Store the result in core's object cache maybe?
2015
	 */
2016
	public static function get_active_plugins() {
2017
		$active_plugins = (array) get_option( 'active_plugins', array() );
2018
2019
		if ( is_multisite() ) {
2020
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
2021
			// whereas active_plugins stores them in the values.
2022
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
2023
			if ( $network_plugins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $network_plugins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2024
				$active_plugins = array_merge( $active_plugins, $network_plugins );
2025
			}
2026
		}
2027
2028
		sort( $active_plugins );
2029
2030
		return array_unique( $active_plugins );
2031
	}
2032
2033
	/**
2034
	 * Gets and parses additional plugin data to send with the heartbeat data
2035
	 *
2036
	 * @since 3.8.1
2037
	 *
2038
	 * @return array Array of plugin data
2039
	 */
2040
	public static function get_parsed_plugin_data() {
2041
		if ( ! function_exists( 'get_plugins' ) ) {
2042
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
2043
		}
2044
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
2045
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
2046
		$active_plugins = self::get_active_plugins();
2047
2048
		$plugins = array();
2049
		foreach ( $all_plugins as $path => $plugin_data ) {
2050
			$plugins[ $path ] = array(
2051
				'is_active' => in_array( $path, $active_plugins ),
2052
				'file'      => $path,
2053
				'name'      => $plugin_data['Name'],
2054
				'version'   => $plugin_data['Version'],
2055
				'author'    => $plugin_data['Author'],
2056
			);
2057
		}
2058
2059
		return $plugins;
2060
	}
2061
2062
	/**
2063
	 * Gets and parses theme data to send with the heartbeat data
2064
	 *
2065
	 * @since 3.8.1
2066
	 *
2067
	 * @return array Array of theme data
2068
	 */
2069
	public static function get_parsed_theme_data() {
2070
		$all_themes  = wp_get_themes( array( 'allowed' => true ) );
2071
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2072
2073
		$themes = array();
2074
		foreach ( $all_themes as $slug => $theme_data ) {
2075
			$theme_headers = array();
2076
			foreach ( $header_keys as $header_key ) {
2077
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2078
			}
2079
2080
			$themes[ $slug ] = array(
2081
				'is_active_theme' => $slug == wp_get_theme()->get_template(),
2082
				'slug'            => $slug,
2083
				'theme_root'      => $theme_data->get_theme_root_uri(),
2084
				'parent'          => $theme_data->parent(),
2085
				'headers'         => $theme_headers,
2086
			);
2087
		}
2088
2089
		return $themes;
2090
	}
2091
2092
	/**
2093
	 * Checks whether a specific plugin is active.
2094
	 *
2095
	 * We don't want to store these in a static variable, in case
2096
	 * there are switch_to_blog() calls involved.
2097
	 */
2098
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2099
		return in_array( $plugin, self::get_active_plugins() );
2100
	}
2101
2102
	/**
2103
	 * Check if Jetpack's Open Graph tags should be used.
2104
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2105
	 *
2106
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2107
	 * @action plugins_loaded
2108
	 * @return null
2109
	 */
2110
	public function check_open_graph() {
2111
		if ( in_array( 'publicize', self::get_active_modules() ) || in_array( 'sharedaddy', self::get_active_modules() ) ) {
2112
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2113
		}
2114
2115
		$active_plugins = self::get_active_plugins();
2116
2117
		if ( ! empty( $active_plugins ) ) {
2118
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2119
				if ( in_array( $plugin, $active_plugins ) ) {
2120
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2121
					break;
2122
				}
2123
			}
2124
		}
2125
2126
		/**
2127
		 * Allow the addition of Open Graph Meta Tags to all pages.
2128
		 *
2129
		 * @since 2.0.3
2130
		 *
2131
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2132
		 */
2133
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2134
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2135
		}
2136
	}
2137
2138
	/**
2139
	 * Check if Jetpack's Twitter tags should be used.
2140
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2141
	 *
2142
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2143
	 * @action plugins_loaded
2144
	 * @return null
2145
	 */
2146
	public function check_twitter_tags() {
2147
2148
		$active_plugins = self::get_active_plugins();
2149
2150
		if ( ! empty( $active_plugins ) ) {
2151
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2152
				if ( in_array( $plugin, $active_plugins ) ) {
2153
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2154
					break;
2155
				}
2156
			}
2157
		}
2158
2159
		/**
2160
		 * Allow Twitter Card Meta tags to be disabled.
2161
		 *
2162
		 * @since 2.6.0
2163
		 *
2164
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2165
		 */
2166
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2167
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2168
		}
2169
	}
2170
2171
	/**
2172
	 * Allows plugins to submit security reports.
2173
	 *
2174
	 * @param string $type         Report type (login_form, backup, file_scanning, spam)
2175
	 * @param string $plugin_file  Plugin __FILE__, so that we can pull plugin data
2176
	 * @param array  $args         See definitions above
2177
	 */
2178
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2179
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2180
	}
2181
2182
	/* Jetpack Options API */
2183
2184
	public static function get_option_names( $type = 'compact' ) {
2185
		return Jetpack_Options::get_option_names( $type );
2186
	}
2187
2188
	/**
2189
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2190
	 *
2191
	 * @param string $name    Option name
2192
	 * @param mixed  $default (optional)
2193
	 */
2194
	public static function get_option( $name, $default = false ) {
2195
		return Jetpack_Options::get_option( $name, $default );
2196
	}
2197
2198
	/**
2199
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2200
	 *
2201
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2202
	 * @param string $name  Option name
2203
	 * @param mixed  $value Option value
2204
	 */
2205
	public static function update_option( $name, $value ) {
2206
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2207
		return Jetpack_Options::update_option( $name, $value );
2208
	}
2209
2210
	/**
2211
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2212
	 *
2213
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2214
	 * @param array $array array( option name => option value, ... )
2215
	 */
2216
	public static function update_options( $array ) {
2217
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2218
		return Jetpack_Options::update_options( $array );
2219
	}
2220
2221
	/**
2222
	 * Deletes the given option.  May be passed multiple option names as an array.
2223
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2224
	 *
2225
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2226
	 * @param string|array $names
2227
	 */
2228
	public static function delete_option( $names ) {
2229
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2230
		return Jetpack_Options::delete_option( $names );
2231
	}
2232
2233
	/**
2234
	 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::update_user_token() instead.
2235
	 *
2236
	 * Enters a user token into the user_tokens option
2237
	 *
2238
	 * @param int    $user_id The user id.
2239
	 * @param string $token The user token.
2240
	 * @param bool   $is_master_user Whether the user is the master user.
2241
	 * @return bool
2242
	 */
2243
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2244
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::update_user_token' );
2245
		return Connection_Utils::update_user_token( $user_id, $token, $is_master_user );
2246
	}
2247
2248
	/**
2249
	 * Returns an array of all PHP files in the specified absolute path.
2250
	 * Equivalent to glob( "$absolute_path/*.php" ).
2251
	 *
2252
	 * @param string $absolute_path The absolute path of the directory to search.
2253
	 * @return array Array of absolute paths to the PHP files.
2254
	 */
2255
	public static function glob_php( $absolute_path ) {
2256
		if ( function_exists( 'glob' ) ) {
2257
			return glob( "$absolute_path/*.php" );
2258
		}
2259
2260
		$absolute_path = untrailingslashit( $absolute_path );
2261
		$files         = array();
2262
		if ( ! $dir = @opendir( $absolute_path ) ) {
2263
			return $files;
2264
		}
2265
2266
		while ( false !== $file = readdir( $dir ) ) {
2267
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2268
				continue;
2269
			}
2270
2271
			$file = "$absolute_path/$file";
2272
2273
			if ( ! is_file( $file ) ) {
2274
				continue;
2275
			}
2276
2277
			$files[] = $file;
2278
		}
2279
2280
		closedir( $dir );
2281
2282
		return $files;
2283
	}
2284
2285
	public static function activate_new_modules( $redirect = false ) {
2286
		if ( ! self::is_active() && ! ( new Status() )->is_offline_mode() ) {
2287
			return;
2288
		}
2289
2290
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2291 View Code Duplication
		if ( ! $jetpack_old_version ) {
2292
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2293
			/** This action is documented in class.jetpack.php */
2294
			do_action( 'updating_jetpack_version', $version, false );
2295
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2296
		}
2297
2298
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2299
2300
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2301
			return;
2302
		}
2303
2304
		$active_modules     = self::get_active_modules();
2305
		$reactivate_modules = array();
2306
		foreach ( $active_modules as $active_module ) {
2307
			$module = self::get_module( $active_module );
2308
			if ( ! isset( $module['changed'] ) ) {
2309
				continue;
2310
			}
2311
2312
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2313
				continue;
2314
			}
2315
2316
			$reactivate_modules[] = $active_module;
2317
			self::deactivate_module( $active_module );
2318
		}
2319
2320
		$new_version = JETPACK__VERSION . ':' . time();
2321
		/** This action is documented in class.jetpack.php */
2322
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2323
		Jetpack_Options::update_options(
2324
			array(
2325
				'version'     => $new_version,
2326
				'old_version' => $jetpack_old_version,
2327
			)
2328
		);
2329
2330
		self::state( 'message', 'modules_activated' );
2331
2332
		self::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules, $redirect );
0 ignored issues
show
JETPACK__VERSION is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2333
2334
		if ( $redirect ) {
2335
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2336
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2337
				$page = $_GET['page'];
2338
			}
2339
2340
			wp_safe_redirect( self::admin_url( 'page=' . $page ) );
2341
			exit;
2342
		}
2343
	}
2344
2345
	/**
2346
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2347
	 * Make sure to tuck away module "library" files in a sub-directory.
2348
	 */
2349
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2350
		static $modules = null;
2351
2352
		if ( ! isset( $modules ) ) {
2353
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2354
			// Use the cache if we're on the front-end and it's available...
2355
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2356
				$modules = $available_modules_option[ JETPACK__VERSION ];
2357
			} else {
2358
				$files = self::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2359
2360
				$modules = array();
2361
2362
				foreach ( $files as $file ) {
2363
					if ( ! $headers = self::get_module( $file ) ) {
2364
						continue;
2365
					}
2366
2367
					$modules[ self::get_module_slug( $file ) ] = $headers['introduced'];
2368
				}
2369
2370
				Jetpack_Options::update_option(
2371
					'available_modules',
2372
					array(
2373
						JETPACK__VERSION => $modules,
2374
					)
2375
				);
2376
			}
2377
		}
2378
2379
		/**
2380
		 * Filters the array of modules available to be activated.
2381
		 *
2382
		 * @since 2.4.0
2383
		 *
2384
		 * @param array $modules Array of available modules.
2385
		 * @param string $min_version Minimum version number required to use modules.
2386
		 * @param string $max_version Maximum version number required to use modules.
2387
		 */
2388
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $min_version.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2389
2390
		if ( ! $min_version && ! $max_version ) {
2391
			return array_keys( $mods );
2392
		}
2393
2394
		$r = array();
2395
		foreach ( $mods as $slug => $introduced ) {
2396
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2397
				continue;
2398
			}
2399
2400
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2401
				continue;
2402
			}
2403
2404
			$r[] = $slug;
2405
		}
2406
2407
		return $r;
2408
	}
2409
2410
	/**
2411
	 * Default modules loaded on activation.
2412
	 */
2413
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2414
		$return = array();
2415
2416
		foreach ( self::get_available_modules( $min_version, $max_version ) as $module ) {
2417
			$module_data = self::get_module( $module );
2418
2419
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2420
				case 'yes':
2421
					$return[] = $module;
2422
					break;
2423
				case 'public':
2424
					if ( Jetpack_Options::get_option( 'public' ) ) {
2425
						$return[] = $module;
2426
					}
2427
					break;
2428
				case 'no':
2429
				default:
2430
					break;
2431
			}
2432
		}
2433
		/**
2434
		 * Filters the array of default modules.
2435
		 *
2436
		 * @since 2.5.0
2437
		 *
2438
		 * @param array $return Array of default modules.
2439
		 * @param string $min_version Minimum version number required to use modules.
2440
		 * @param string $max_version Maximum version number required to use modules.
2441
		 */
2442
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $min_version.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2443
	}
2444
2445
	/**
2446
	 * Checks activated modules during auto-activation to determine
2447
	 * if any of those modules are being deprecated.  If so, close
2448
	 * them out, and add any replacement modules.
2449
	 *
2450
	 * Runs at priority 99 by default.
2451
	 *
2452
	 * This is run late, so that it can still activate a module if
2453
	 * the new module is a replacement for another that the user
2454
	 * currently has active, even if something at the normal priority
2455
	 * would kibosh everything.
2456
	 *
2457
	 * @since 2.6
2458
	 * @uses jetpack_get_default_modules filter
2459
	 * @param array $modules
2460
	 * @return array
2461
	 */
2462
	function handle_deprecated_modules( $modules ) {
2463
		$deprecated_modules = array(
2464
			'debug'            => null,  // Closed out and moved to the debugger library.
2465
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2466
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2467
			'minileven'        => null,  // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2468
		);
2469
2470
		// Don't activate SSO if they never completed activating WPCC.
2471
		if ( self::is_module_active( 'wpcc' ) ) {
2472
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2473
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2474
				$deprecated_modules['wpcc'] = null;
2475
			}
2476
		}
2477
2478
		foreach ( $deprecated_modules as $module => $replacement ) {
2479
			if ( self::is_module_active( $module ) ) {
2480
				self::deactivate_module( $module );
2481
				if ( $replacement ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $replacement of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2482
					$modules[] = $replacement;
2483
				}
2484
			}
2485
		}
2486
2487
		return array_unique( $modules );
2488
	}
2489
2490
	/**
2491
	 * Checks activated plugins during auto-activation to determine
2492
	 * if any of those plugins are in the list with a corresponding module
2493
	 * that is not compatible with the plugin. The module will not be allowed
2494
	 * to auto-activate.
2495
	 *
2496
	 * @since 2.6
2497
	 * @uses jetpack_get_default_modules filter
2498
	 * @param array $modules
2499
	 * @return array
2500
	 */
2501
	function filter_default_modules( $modules ) {
2502
2503
		$active_plugins = self::get_active_plugins();
2504
2505
		if ( ! empty( $active_plugins ) ) {
2506
2507
			// For each module we'd like to auto-activate...
2508
			foreach ( $modules as $key => $module ) {
2509
				// If there are potential conflicts for it...
2510
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2511
					// For each potential conflict...
2512
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2513
						// If that conflicting plugin is active...
2514
						if ( in_array( $plugin, $active_plugins ) ) {
2515
							// Remove that item from being auto-activated.
2516
							unset( $modules[ $key ] );
2517
						}
2518
					}
2519
				}
2520
			}
2521
		}
2522
2523
		return $modules;
2524
	}
2525
2526
	/**
2527
	 * Extract a module's slug from its full path.
2528
	 */
2529
	public static function get_module_slug( $file ) {
2530
		return str_replace( '.php', '', basename( $file ) );
2531
	}
2532
2533
	/**
2534
	 * Generate a module's path from its slug.
2535
	 */
2536
	public static function get_module_path( $slug ) {
2537
		/**
2538
		 * Filters the path of a modules.
2539
		 *
2540
		 * @since 7.4.0
2541
		 *
2542
		 * @param array $return The absolute path to a module's root php file
2543
		 * @param string $slug The module slug
2544
		 */
2545
		return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $slug.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2546
	}
2547
2548
	/**
2549
	 * Load module data from module file. Headers differ from WordPress
2550
	 * plugin headers to avoid them being identified as standalone
2551
	 * plugins on the WordPress plugins page.
2552
	 */
2553
	public static function get_module( $module ) {
2554
		$headers = array(
2555
			'name'                      => 'Module Name',
2556
			'description'               => 'Module Description',
2557
			'sort'                      => 'Sort Order',
2558
			'recommendation_order'      => 'Recommendation Order',
2559
			'introduced'                => 'First Introduced',
2560
			'changed'                   => 'Major Changes In',
2561
			'deactivate'                => 'Deactivate',
2562
			'free'                      => 'Free',
2563
			'requires_connection'       => 'Requires Connection',
2564
			'auto_activate'             => 'Auto Activate',
2565
			'module_tags'               => 'Module Tags',
2566
			'feature'                   => 'Feature',
2567
			'additional_search_queries' => 'Additional Search Queries',
2568
			'plan_classes'              => 'Plans',
2569
		);
2570
2571
		$file = self::get_module_path( self::get_module_slug( $module ) );
2572
2573
		$mod = self::get_file_data( $file, $headers );
2574
		if ( empty( $mod['name'] ) ) {
2575
			return false;
2576
		}
2577
2578
		$mod['sort']                 = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2579
		$mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2580
		$mod['deactivate']           = empty( $mod['deactivate'] );
2581
		$mod['free']                 = empty( $mod['free'] );
2582
		$mod['requires_connection']  = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2583
2584
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2585
			$mod['auto_activate'] = 'No';
2586
		} else {
2587
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2588
		}
2589
2590
		if ( $mod['module_tags'] ) {
2591
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2592
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2593
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2594
		} else {
2595
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2596
		}
2597
2598 View Code Duplication
		if ( $mod['plan_classes'] ) {
2599
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2600
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2601
		} else {
2602
			$mod['plan_classes'] = array( 'free' );
2603
		}
2604
2605 View Code Duplication
		if ( $mod['feature'] ) {
2606
			$mod['feature'] = explode( ',', $mod['feature'] );
2607
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2608
		} else {
2609
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2610
		}
2611
2612
		/**
2613
		 * Filters the feature array on a module.
2614
		 *
2615
		 * This filter allows you to control where each module is filtered: Recommended,
2616
		 * and the default "Other" listing.
2617
		 *
2618
		 * @since 3.5.0
2619
		 *
2620
		 * @param array   $mod['feature'] The areas to feature this module:
2621
		 *     'Recommended' shows on the main Jetpack admin screen.
2622
		 *     'Other' should be the default if no other value is in the array.
2623
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2624
		 * @param array   $mod All the currently assembled module data.
2625
		 */
2626
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $module.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2627
2628
		/**
2629
		 * Filter the returned data about a module.
2630
		 *
2631
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2632
		 * so please be careful.
2633
		 *
2634
		 * @since 3.6.0
2635
		 *
2636
		 * @param array   $mod    The details of the requested module.
2637
		 * @param string  $module The slug of the module, e.g. sharedaddy
2638
		 * @param string  $file   The path to the module source file.
2639
		 */
2640
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $module.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2641
	}
2642
2643
	/**
2644
	 * Like core's get_file_data implementation, but caches the result.
2645
	 */
2646
	public static function get_file_data( $file, $headers ) {
2647
		// Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2648
		$file_name = basename( $file );
2649
2650
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2651
2652
		$file_data_option = get_transient( $cache_key );
2653
2654
		if ( ! is_array( $file_data_option ) ) {
2655
			delete_transient( $cache_key );
2656
			$file_data_option = false;
2657
		}
2658
2659
		if ( false === $file_data_option ) {
2660
			$file_data_option = array();
2661
		}
2662
2663
		$key           = md5( $file_name . serialize( $headers ) );
2664
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2665
2666
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2667
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2668
			return $file_data_option[ $key ];
2669
		}
2670
2671
		$data = get_file_data( $file, $headers );
2672
2673
		$file_data_option[ $key ] = $data;
2674
2675
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2676
2677
		return $data;
2678
	}
2679
2680
	/**
2681
	 * Return translated module tag.
2682
	 *
2683
	 * @param string $tag Tag as it appears in each module heading.
2684
	 *
2685
	 * @return mixed
2686
	 */
2687
	public static function translate_module_tag( $tag ) {
2688
		return jetpack_get_module_i18n_tag( $tag );
2689
	}
2690
2691
	/**
2692
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2693
	 *
2694
	 * @since 3.9.2
2695
	 *
2696
	 * @param array $modules
2697
	 *
2698
	 * @return string|void
2699
	 */
2700
	public static function get_translated_modules( $modules ) {
2701
		foreach ( $modules as $index => $module ) {
2702
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2703
			if ( isset( $module['name'] ) ) {
2704
				$modules[ $index ]['name'] = $i18n_module['name'];
2705
			}
2706
			if ( isset( $module['description'] ) ) {
2707
				$modules[ $index ]['description']       = $i18n_module['description'];
2708
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2709
			}
2710
		}
2711
		return $modules;
2712
	}
2713
2714
	/**
2715
	 * Get a list of activated modules as an array of module slugs.
2716
	 */
2717
	public static function get_active_modules() {
2718
		$active = Jetpack_Options::get_option( 'active_modules' );
2719
2720
		if ( ! is_array( $active ) ) {
2721
			$active = array();
2722
		}
2723
2724
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2725
			$active[] = 'vaultpress';
2726
		} else {
2727
			$active = array_diff( $active, array( 'vaultpress' ) );
2728
		}
2729
2730
		// If protect is active on the main site of a multisite, it should be active on all sites.
2731
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2732
			$active[] = 'protect';
2733
		}
2734
2735
		/**
2736
		 * Allow filtering of the active modules.
2737
		 *
2738
		 * Gives theme and plugin developers the power to alter the modules that
2739
		 * are activated on the fly.
2740
		 *
2741
		 * @since 5.8.0
2742
		 *
2743
		 * @param array $active Array of active module slugs.
2744
		 */
2745
		$active = apply_filters( 'jetpack_active_modules', $active );
2746
2747
		return array_unique( $active );
2748
	}
2749
2750
	/**
2751
	 * Check whether or not a Jetpack module is active.
2752
	 *
2753
	 * @param string $module The slug of a Jetpack module.
2754
	 * @return bool
2755
	 *
2756
	 * @static
2757
	 */
2758
	public static function is_module_active( $module ) {
2759
		return in_array( $module, self::get_active_modules() );
2760
	}
2761
2762
	public static function is_module( $module ) {
2763
		return ! empty( $module ) && ! validate_file( $module, self::get_available_modules() );
2764
	}
2765
2766
	/**
2767
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2768
	 *
2769
	 * @param bool $catch True to start catching, False to stop.
2770
	 *
2771
	 * @static
2772
	 */
2773
	public static function catch_errors( $catch ) {
2774
		static $display_errors, $error_reporting;
2775
2776
		if ( $catch ) {
2777
			$display_errors  = @ini_set( 'display_errors', 1 );
2778
			$error_reporting = @error_reporting( E_ALL );
2779
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2780
		} else {
2781
			@ini_set( 'display_errors', $display_errors );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2782
			@error_reporting( $error_reporting );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2783
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2784
		}
2785
	}
2786
2787
	/**
2788
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2789
	 */
2790
	public static function catch_errors_on_shutdown() {
2791
		self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2792
	}
2793
2794
	/**
2795
	 * Rewrite any string to make paths easier to read.
2796
	 *
2797
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2798
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2799
	 *
2800
	 * @param $string
2801
	 * @return mixed
2802
	 */
2803
	public static function alias_directories( $string ) {
2804
		// ABSPATH has a trailing slash.
2805
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2806
		// WP_CONTENT_DIR does not have a trailing slash.
2807
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2808
2809
		return $string;
2810
	}
2811
2812
	public static function activate_default_modules(
2813
		$min_version = false,
2814
		$max_version = false,
2815
		$other_modules = array(),
2816
		$redirect = null,
2817
		$send_state_messages = null
2818
	) {
2819
		$jetpack = self::init();
2820
2821
		if ( is_null( $redirect ) ) {
2822
			if (
2823
				( defined( 'REST_REQUEST' ) && REST_REQUEST )
2824
			||
2825
				( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
2826
			||
2827
				( defined( 'WP_CLI' ) && WP_CLI )
2828
			||
2829
				( defined( 'DOING_CRON' ) && DOING_CRON )
2830
			||
2831
				( defined( 'DOING_AJAX' ) && DOING_AJAX )
2832
			) {
2833
				$redirect = false;
2834
			} elseif ( is_admin() ) {
2835
				$redirect = true;
2836
			} else {
2837
				$redirect = false;
2838
			}
2839
		}
2840
2841
		if ( is_null( $send_state_messages ) ) {
2842
			$send_state_messages = current_user_can( 'jetpack_activate_modules' );
2843
		}
2844
2845
		$modules = self::get_default_modules( $min_version, $max_version );
2846
		$modules = array_merge( $other_modules, $modules );
2847
2848
		// Look for standalone plugins and disable if active.
2849
2850
		$to_deactivate = array();
2851
		foreach ( $modules as $module ) {
2852
			if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2853
				$to_deactivate[ $module ] = $jetpack->plugins_to_deactivate[ $module ];
2854
			}
2855
		}
2856
2857
		$deactivated = array();
2858
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2859
			list( $probable_file, $probable_title ) = $deactivate_me;
2860
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2861
				$deactivated[] = $module;
2862
			}
2863
		}
2864
2865
		if ( $deactivated ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2866
			if ( $send_state_messages ) {
2867
				self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2868
			}
2869
2870
			if ( $redirect ) {
2871
				$url = add_query_arg(
2872
					array(
2873
						'action'   => 'activate_default_modules',
2874
						'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2875
					),
2876
					add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2877
				);
2878
				wp_safe_redirect( $url );
2879
				exit;
2880
			}
2881
		}
2882
2883
		/**
2884
		 * Fires before default modules are activated.
2885
		 *
2886
		 * @since 1.9.0
2887
		 *
2888
		 * @param string $min_version Minimum version number required to use modules.
2889
		 * @param string $max_version Maximum version number required to use modules.
2890
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2891
		 */
2892
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2893
2894
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2895
		if ( $send_state_messages ) {
2896
			self::restate();
2897
			self::catch_errors( true );
2898
		}
2899
2900
		$active = self::get_active_modules();
2901
2902
		foreach ( $modules as $module ) {
2903
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2904
				$active[] = $module;
2905
				self::update_active_modules( $active );
2906
				continue;
2907
			}
2908
2909
			if ( $send_state_messages && in_array( $module, $active ) ) {
2910
				$module_info = self::get_module( $module );
2911 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2912
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2913
					if ( $active_state = self::state( $state ) ) {
2914
						$active_state = explode( ',', $active_state );
2915
					} else {
2916
						$active_state = array();
2917
					}
2918
					$active_state[] = $module;
2919
					self::state( $state, implode( ',', $active_state ) );
2920
				}
2921
				continue;
2922
			}
2923
2924
			$file = self::get_module_path( $module );
2925
			if ( ! file_exists( $file ) ) {
2926
				continue;
2927
			}
2928
2929
			// we'll override this later if the plugin can be included without fatal error
2930
			if ( $redirect ) {
2931
				wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
2932
			}
2933
2934
			if ( $send_state_messages ) {
2935
				self::state( 'error', 'module_activation_failed' );
2936
				self::state( 'module', $module );
2937
			}
2938
2939
			ob_start();
2940
			require_once $file;
2941
2942
			$active[] = $module;
2943
2944 View Code Duplication
			if ( $send_state_messages ) {
2945
2946
				$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2947
				if ( $active_state = self::state( $state ) ) {
2948
					$active_state = explode( ',', $active_state );
2949
				} else {
2950
					$active_state = array();
2951
				}
2952
				$active_state[] = $module;
2953
				self::state( $state, implode( ',', $active_state ) );
2954
			}
2955
2956
			self::update_active_modules( $active );
2957
2958
			ob_end_clean();
2959
		}
2960
2961
		if ( $send_state_messages ) {
2962
			self::state( 'error', false );
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2963
			self::state( 'module', false );
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2964
		}
2965
2966
		self::catch_errors( false );
2967
		/**
2968
		 * Fires when default modules are activated.
2969
		 *
2970
		 * @since 1.9.0
2971
		 *
2972
		 * @param string $min_version Minimum version number required to use modules.
2973
		 * @param string $max_version Maximum version number required to use modules.
2974
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2975
		 */
2976
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2977
	}
2978
2979
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2980
		/**
2981
		 * Fires before a module is activated.
2982
		 *
2983
		 * @since 2.6.0
2984
		 *
2985
		 * @param string $module Module slug.
2986
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2987
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2988
		 */
2989
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2990
2991
		$jetpack = self::init();
2992
2993
		if ( ! strlen( $module ) ) {
2994
			return false;
2995
		}
2996
2997
		if ( ! self::is_module( $module ) ) {
2998
			return false;
2999
		}
3000
3001
		// If it's already active, then don't do it again
3002
		$active = self::get_active_modules();
3003
		foreach ( $active as $act ) {
3004
			if ( $act == $module ) {
3005
				return true;
3006
			}
3007
		}
3008
3009
		$module_data = self::get_module( $module );
3010
3011
		$is_offline_mode = ( new Status() )->is_offline_mode();
3012
		if ( ! self::is_active() ) {
3013
			if ( ! $is_offline_mode && ! self::is_onboarding() ) {
3014
				return false;
3015
			}
3016
3017
			// If we're not connected but in offline mode, make sure the module doesn't require a connection.
3018
			if ( $is_offline_mode && $module_data['requires_connection'] ) {
3019
				return false;
3020
			}
3021
		}
3022
3023
		// Check and see if the old plugin is active
3024
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3025
			// Deactivate the old plugin
3026
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
3027
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
3028
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
3029
				self::state( 'deactivated_plugins', $module );
3030
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
3031
				exit;
3032
			}
3033
		}
3034
3035
		// Protect won't work with mis-configured IPs
3036
		if ( 'protect' === $module ) {
3037
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
3038
			if ( ! jetpack_protect_get_ip() ) {
3039
				self::state( 'message', 'protect_misconfigured_ip' );
3040
				return false;
3041
			}
3042
		}
3043
3044
		if ( ! Jetpack_Plan::supports( $module ) ) {
3045
			return false;
3046
		}
3047
3048
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
3049
		self::state( 'module', $module );
3050
		self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
3051
3052
		self::catch_errors( true );
3053
		ob_start();
3054
		require self::get_module_path( $module );
3055
		/** This action is documented in class.jetpack.php */
3056
		do_action( 'jetpack_activate_module', $module );
3057
		$active[] = $module;
3058
		self::update_active_modules( $active );
3059
3060
		self::state( 'error', false ); // the override
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3061
		ob_end_clean();
3062
		self::catch_errors( false );
3063
3064
		if ( $redirect ) {
3065
			wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3066
		}
3067
		if ( $exit ) {
3068
			exit;
3069
		}
3070
		return true;
3071
	}
3072
3073
	function activate_module_actions( $module ) {
3074
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3075
	}
3076
3077
	public static function deactivate_module( $module ) {
3078
		/**
3079
		 * Fires when a module is deactivated.
3080
		 *
3081
		 * @since 1.9.0
3082
		 *
3083
		 * @param string $module Module slug.
3084
		 */
3085
		do_action( 'jetpack_pre_deactivate_module', $module );
3086
3087
		$jetpack = self::init();
0 ignored issues
show
$jetpack is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3088
3089
		$active = self::get_active_modules();
3090
		$new    = array_filter( array_diff( $active, (array) $module ) );
3091
3092
		return self::update_active_modules( $new );
3093
	}
3094
3095
	public static function enable_module_configurable( $module ) {
3096
		$module = self::get_module_slug( $module );
3097
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3098
	}
3099
3100
	/**
3101
	 * Composes a module configure URL. It uses Jetpack settings search as default value
3102
	 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3103
	 *
3104
	 * @param string $module Module slug
3105
	 * @return string $url module configuration URL
3106
	 */
3107
	public static function module_configuration_url( $module ) {
3108
		$module      = self::get_module_slug( $module );
3109
		$default_url = self::admin_url() . "#/settings?term=$module";
3110
		/**
3111
		 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3112
		 *
3113
		 * @since 6.9.0
3114
		 *
3115
		 * @param string $default_url Default url, which redirects to jetpack settings page.
3116
		 */
3117
		$url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3118
3119
		return $url;
3120
	}
3121
3122
	/* Installation */
3123
	public static function bail_on_activation( $message, $deactivate = true ) {
3124
		?>
3125
<!doctype html>
3126
<html>
3127
<head>
3128
<meta charset="<?php bloginfo( 'charset' ); ?>">
3129
<style>
3130
* {
3131
	text-align: center;
3132
	margin: 0;
3133
	padding: 0;
3134
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3135
}
3136
p {
3137
	margin-top: 1em;
3138
	font-size: 18px;
3139
}
3140
</style>
3141
<body>
3142
<p><?php echo esc_html( $message ); ?></p>
3143
</body>
3144
</html>
3145
		<?php
3146
		if ( $deactivate ) {
3147
			$plugins = get_option( 'active_plugins' );
3148
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3149
			$update  = false;
3150
			foreach ( $plugins as $i => $plugin ) {
3151
				if ( $plugin === $jetpack ) {
3152
					$plugins[ $i ] = false;
3153
					$update        = true;
3154
				}
3155
			}
3156
3157
			if ( $update ) {
3158
				update_option( 'active_plugins', array_filter( $plugins ) );
3159
			}
3160
		}
3161
		exit;
3162
	}
3163
3164
	/**
3165
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3166
	 *
3167
	 * @static
3168
	 */
3169
	public static function plugin_activation( $network_wide ) {
3170
		Jetpack_Options::update_option( 'activated', 1 );
3171
3172
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3173
			self::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3174
		}
3175
3176
		if ( $network_wide ) {
3177
			self::state( 'network_nag', true );
0 ignored issues
show
true is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3178
		}
3179
3180
		// For firing one-off events (notices) immediately after activation
3181
		set_transient( 'activated_jetpack', true, 0.1 * MINUTE_IN_SECONDS );
3182
3183
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3184
3185
		Health::on_jetpack_activated();
3186
3187
		self::plugin_initialize();
3188
	}
3189
3190
	public static function get_activation_source( $referer_url ) {
3191
3192
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3193
			return array( 'wp-cli', null );
3194
		}
3195
3196
		$referer = wp_parse_url( $referer_url );
3197
3198
		$source_type  = 'unknown';
3199
		$source_query = null;
3200
3201
		if ( ! is_array( $referer ) ) {
3202
			return array( $source_type, $source_query );
3203
		}
3204
3205
		$plugins_path         = wp_parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
3206
		$plugins_install_path = wp_parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
3207
3208
		if ( isset( $referer['query'] ) ) {
3209
			parse_str( $referer['query'], $query_parts );
3210
		} else {
3211
			$query_parts = array();
3212
		}
3213
3214
		if ( $plugins_path === $referer['path'] ) {
3215
			$source_type = 'list';
3216
		} elseif ( $plugins_install_path === $referer['path'] ) {
3217
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3218
			switch ( $tab ) {
3219
				case 'popular':
3220
					$source_type = 'popular';
3221
					break;
3222
				case 'recommended':
3223
					$source_type = 'recommended';
3224
					break;
3225
				case 'favorites':
3226
					$source_type = 'favorites';
3227
					break;
3228
				case 'search':
3229
					$source_type  = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3230
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3231
					break;
3232
				default:
3233
					$source_type = 'featured';
3234
			}
3235
		}
3236
3237
		return array( $source_type, $source_query );
3238
	}
3239
3240
	/**
3241
	 * Runs before bumping version numbers up to a new version
3242
	 *
3243
	 * @param string $version    Version:timestamp.
3244
	 * @param string $old_version Old Version:timestamp or false if not set yet.
3245
	 */
3246
	public static function do_version_bump( $version, $old_version ) {
3247
		if ( $old_version ) { // For existing Jetpack installations.
3248
3249
			// If a front end page is visited after the update, the 'wp' action will fire.
3250
			add_action( 'wp', 'Jetpack::set_update_modal_display' );
3251
3252
			// If an admin page is visited after the update, the 'current_screen' action will fire.
3253
			add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3254
		}
3255
	}
3256
3257
	/**
3258
	 * Sets the display_update_modal state.
3259
	 */
3260
	public static function set_update_modal_display() {
3261
		self::state( 'display_update_modal', true );
0 ignored issues
show
true is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3262
	}
3263
3264
	/**
3265
	 * Sets the internal version number and activation state.
3266
	 *
3267
	 * @static
3268
	 */
3269
	public static function plugin_initialize() {
3270
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3271
			Jetpack_Options::update_option( 'activated', 2 );
3272
		}
3273
3274 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3275
			$version = $old_version = JETPACK__VERSION . ':' . time();
3276
			/** This action is documented in class.jetpack.php */
3277
			do_action( 'updating_jetpack_version', $version, false );
3278
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3279
		}
3280
3281
		self::load_modules();
3282
3283
		Jetpack_Options::delete_option( 'do_activate' );
3284
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3285
	}
3286
3287
	/**
3288
	 * Removes all connection options
3289
	 *
3290
	 * @static
3291
	 */
3292
	public static function plugin_deactivation() {
3293
		require_once ABSPATH . '/wp-admin/includes/plugin.php';
3294
		$tracking = new Tracking();
3295
		$tracking->record_user_event( 'deactivate_plugin', array() );
3296
		if ( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3297
			Jetpack_Network::init()->deactivate();
3298
		} else {
3299
			self::disconnect( false );
3300
			// Jetpack_Heartbeat::init()->deactivate();
3301
		}
3302
	}
3303
3304
	/**
3305
	 * Disconnects from the Jetpack servers.
3306
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3307
	 *
3308
	 * @static
3309
	 */
3310
	public static function disconnect( $update_activated_state = true ) {
3311
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3312
3313
		$connection = self::connection();
3314
		$connection->clean_nonces( true );
3315
3316
		// If the site is in an IDC because sync is not allowed,
3317
		// let's make sure to not disconnect the production site.
3318
		if ( ! self::validate_sync_error_idc_option() ) {
3319
			$tracking = new Tracking();
3320
			$tracking->record_user_event( 'disconnect_site', array() );
3321
3322
			$connection->disconnect_site_wpcom( true );
3323
		}
3324
3325
		$connection->delete_all_connection_tokens( true );
3326
		Jetpack_IDC::clear_all_idc_options();
3327
3328
		if ( $update_activated_state ) {
3329
			Jetpack_Options::update_option( 'activated', 4 );
3330
		}
3331
3332
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3333
			// Check then record unique disconnection if site has never been disconnected previously
3334
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3335
				$jetpack_unique_connection['disconnected'] = 1;
3336
			} else {
3337
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3338
					// track unique disconnect
3339
					$jetpack = self::init();
3340
3341
					$jetpack->stat( 'connections', 'unique-disconnect' );
3342
					$jetpack->do_stats( 'server_side' );
3343
				}
3344
				// increment number of times disconnected
3345
				$jetpack_unique_connection['disconnected'] += 1;
3346
			}
3347
3348
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3349
		}
3350
3351
		// Delete all the sync related data. Since it could be taking up space.
3352
		Sender::get_instance()->uninstall();
3353
3354
	}
3355
3356
	/**
3357
	 * Unlinks the current user from the linked WordPress.com user.
3358
	 *
3359
	 * @deprecated since 7.7
3360
	 * @see Automattic\Jetpack\Connection\Manager::disconnect_user()
3361
	 *
3362
	 * @param Integer $user_id the user identifier.
0 ignored issues
show
Should the type for parameter $user_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3363
	 * @return Boolean Whether the disconnection of the user was successful.
3364
	 */
3365
	public static function unlink_user( $user_id = null ) {
3366
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::disconnect_user' );
3367
		return Connection_Manager::disconnect_user( $user_id );
3368
	}
3369
3370
	/**
3371
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3372
	 */
3373
	public static function try_registration() {
3374
		$terms_of_service = new Terms_Of_Service();
3375
		// The user has agreed to the TOS at some point by now.
3376
		$terms_of_service->agree();
3377
3378
		// Let's get some testing in beta versions and such.
3379
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3380
			// Before attempting to connect, let's make sure that the domains are viable.
3381
			$domains_to_check = array_unique(
3382
				array(
3383
					'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
3384
					'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
3385
				)
3386
			);
3387
			foreach ( $domains_to_check as $domain ) {
3388
				$result = self::connection()->is_usable_domain( $domain );
0 ignored issues
show
It seems like $domain defined by $domain on line 3387 can also be of type false; however, Automattic\Jetpack\Conne...ger::is_usable_domain() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
3389
				if ( is_wp_error( $result ) ) {
3390
					return $result;
3391
				}
3392
			}
3393
		}
3394
3395
		$result = self::register();
3396
3397
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3398
		if ( ! $result || is_wp_error( $result ) ) {
3399
			return $result;
3400
		} else {
3401
			return true;
3402
		}
3403
	}
3404
3405
	/**
3406
	 * Tracking an internal event log. Try not to put too much chaff in here.
3407
	 *
3408
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3409
	 */
3410
	public static function log( $code, $data = null ) {
3411
		// only grab the latest 200 entries
3412
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3413
3414
		// Append our event to the log
3415
		$log_entry = array(
3416
			'time'    => time(),
3417
			'user_id' => get_current_user_id(),
3418
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3419
			'code'    => $code,
3420
		);
3421
		// Don't bother storing it unless we've got some.
3422
		if ( ! is_null( $data ) ) {
3423
			$log_entry['data'] = $data;
3424
		}
3425
		$log[] = $log_entry;
3426
3427
		// Try add_option first, to make sure it's not autoloaded.
3428
		// @todo: Add an add_option method to Jetpack_Options
3429
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3430
			Jetpack_Options::update_option( 'log', $log );
3431
		}
3432
3433
		/**
3434
		 * Fires when Jetpack logs an internal event.
3435
		 *
3436
		 * @since 3.0.0
3437
		 *
3438
		 * @param array $log_entry {
3439
		 *  Array of details about the log entry.
3440
		 *
3441
		 *  @param string time Time of the event.
3442
		 *  @param int user_id ID of the user who trigerred the event.
3443
		 *  @param int blog_id Jetpack Blog ID.
3444
		 *  @param string code Unique name for the event.
3445
		 *  @param string data Data about the event.
3446
		 * }
3447
		 */
3448
		do_action( 'jetpack_log_entry', $log_entry );
3449
	}
3450
3451
	/**
3452
	 * Get the internal event log.
3453
	 *
3454
	 * @param $event (string) - only return the specific log events
3455
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3456
	 *
3457
	 * @return array of log events || WP_Error for invalid params
3458
	 */
3459
	public static function get_log( $event = false, $num = false ) {
3460
		if ( $event && ! is_string( $event ) ) {
3461
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with __('First param must be ...g or empty', 'jetpack').

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3462
		}
3463
3464
		if ( $num && ! is_numeric( $num ) ) {
3465
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with __('Second param must be...c or empty', 'jetpack').

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3466
		}
3467
3468
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3469
3470
		// If nothing set - act as it did before, otherwise let's start customizing the output
3471
		if ( ! $num && ! $event ) {
3472
			return $entire_log;
3473
		} else {
3474
			$entire_log = array_reverse( $entire_log );
3475
		}
3476
3477
		$custom_log_output = array();
3478
3479
		if ( $event ) {
3480
			foreach ( $entire_log as $log_event ) {
3481
				if ( $event == $log_event['code'] ) {
3482
					$custom_log_output[] = $log_event;
3483
				}
3484
			}
3485
		} else {
3486
			$custom_log_output = $entire_log;
3487
		}
3488
3489
		if ( $num ) {
3490
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3491
		}
3492
3493
		return $custom_log_output;
3494
	}
3495
3496
	/**
3497
	 * Log modification of important settings.
3498
	 */
3499
	public static function log_settings_change( $option, $old_value, $value ) {
3500
		switch ( $option ) {
3501
			case 'jetpack_sync_non_public_post_stati':
3502
				self::log( $option, $value );
3503
				break;
3504
		}
3505
	}
3506
3507
	/**
3508
	 * Return stat data for WPCOM sync
3509
	 */
3510
	public static function get_stat_data( $encode = true, $extended = true ) {
3511
		$data = Jetpack_Heartbeat::generate_stats_array();
3512
3513
		if ( $extended ) {
3514
			$additional_data = self::get_additional_stat_data();
3515
			$data            = array_merge( $data, $additional_data );
3516
		}
3517
3518
		if ( $encode ) {
3519
			return json_encode( $data );
3520
		}
3521
3522
		return $data;
3523
	}
3524
3525
	/**
3526
	 * Get additional stat data to sync to WPCOM
3527
	 */
3528
	public static function get_additional_stat_data( $prefix = '' ) {
3529
		$return[ "{$prefix}themes" ]        = self::get_parsed_theme_data();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
3530
		$return[ "{$prefix}plugins-extra" ] = self::get_parsed_plugin_data();
3531
		$return[ "{$prefix}users" ]         = (int) self::get_site_user_count();
3532
		$return[ "{$prefix}site-count" ]    = 0;
3533
3534
		if ( function_exists( 'get_blog_count' ) ) {
3535
			$return[ "{$prefix}site-count" ] = get_blog_count();
3536
		}
3537
		return $return;
3538
	}
3539
3540
	private static function get_site_user_count() {
3541
		global $wpdb;
3542
3543
		if ( function_exists( 'wp_is_large_network' ) ) {
3544
			if ( wp_is_large_network( 'users' ) ) {
3545
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3546
			}
3547
		}
3548
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3549
			// It wasn't there, so regenerate the data and save the transient
3550
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3551
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3552
		}
3553
		return $user_count;
3554
	}
3555
3556
	/* Admin Pages */
3557
3558
	function admin_init() {
3559
		// If the plugin is not connected, display a connect message.
3560
		if (
3561
			// the plugin was auto-activated and needs its candy
3562
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3563
		||
3564
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3565
			! Jetpack_Options::get_option( 'activated' )
3566
		) {
3567
			self::plugin_initialize();
3568
		}
3569
3570
		$is_offline_mode = ( new Status() )->is_offline_mode();
3571
		if ( ! self::is_active() && ! $is_offline_mode ) {
3572
			Jetpack_Connection_Banner::init();
3573
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3574
			// Upgrade: 1.1 -> 1.1.1
3575
			// Check and see if host can verify the Jetpack servers' SSL certificate
3576
			$args = array();
3577
			Client::_wp_remote_request( self::connection()->api_url( 'test' ), $args, true );
3578
		}
3579
3580
		Jetpack_Wizard_Banner::init();
3581
3582
		if ( current_user_can( 'manage_options' ) && ! self::permit_ssl() ) {
3583
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3584
		}
3585
3586
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3587
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3588
		add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3589
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3590
3591
		if ( self::is_active() || $is_offline_mode ) {
3592
			// Artificially throw errors in certain specific cases during plugin activation.
3593
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3594
		}
3595
3596
		// Add custom column in wp-admin/users.php to show whether user is linked.
3597
		add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3598
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3599
		add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3600
	}
3601
3602
	function admin_body_class( $admin_body_class = '' ) {
3603
		$classes = explode( ' ', trim( $admin_body_class ) );
3604
3605
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3606
3607
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3608
		return " $admin_body_class ";
3609
	}
3610
3611
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3612
		return $admin_body_class . ' jetpack-pagestyles ';
3613
	}
3614
3615
	/**
3616
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3617
	 * This function artificially throws errors for such cases (per a specific list).
3618
	 *
3619
	 * @param string $plugin The activated plugin.
3620
	 */
3621
	function throw_error_on_activate_plugin( $plugin ) {
3622
		$active_modules = self::get_active_modules();
3623
3624
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3625
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3626
			$throw = false;
3627
3628
			// Try and make sure it really was the stats plugin
3629
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3630
				if ( 'stats.php' == basename( $plugin ) ) {
3631
					$throw = true;
3632
				}
3633
			} else {
3634
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3635
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3636
					$throw = true;
3637
				}
3638
			}
3639
3640
			if ( $throw ) {
3641
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3642
			}
3643
		}
3644
	}
3645
3646
	function intercept_plugin_error_scrape_init() {
3647
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3648
	}
3649
3650
	function intercept_plugin_error_scrape( $action, $result ) {
3651
		if ( ! $result ) {
3652
			return;
3653
		}
3654
3655
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3656
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3657
				self::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3658
			}
3659
		}
3660
	}
3661
3662
	/**
3663
	 * Register the remote file upload request handlers, if needed.
3664
	 *
3665
	 * @access public
3666
	 */
3667
	public function add_remote_request_handlers() {
3668
		// Remote file uploads are allowed only via AJAX requests.
3669
		if ( ! is_admin() || ! Constants::get_constant( 'DOING_AJAX' ) ) {
3670
			return;
3671
		}
3672
3673
		// Remote file uploads are allowed only for a set of specific AJAX actions.
3674
		$remote_request_actions = array(
3675
			'jetpack_upload_file',
3676
			'jetpack_update_file',
3677
		);
3678
3679
		// phpcs:ignore WordPress.Security.NonceVerification
3680
		if ( ! isset( $_POST['action'] ) || ! in_array( $_POST['action'], $remote_request_actions, true ) ) {
3681
			return;
3682
		}
3683
3684
		// Require Jetpack authentication for the remote file upload AJAX requests.
3685
		if ( ! $this->connection_manager ) {
3686
			$this->connection_manager = new Connection_Manager();
3687
		}
3688
3689
		$this->connection_manager->require_jetpack_authentication();
3690
3691
		// Register the remote file upload AJAX handlers.
3692
		foreach ( $remote_request_actions as $action ) {
3693
			add_action( "wp_ajax_nopriv_{$action}", array( $this, 'remote_request_handlers' ) );
3694
		}
3695
	}
3696
3697
	/**
3698
	 * Handler for Jetpack remote file uploads.
3699
	 *
3700
	 * @access public
3701
	 */
3702
	public function remote_request_handlers() {
3703
		$action = current_filter();
0 ignored issues
show
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3704
3705
		switch ( current_filter() ) {
3706
			case 'wp_ajax_nopriv_jetpack_upload_file':
3707
				$response = $this->upload_handler();
3708
				break;
3709
3710
			case 'wp_ajax_nopriv_jetpack_update_file':
3711
				$response = $this->upload_handler( true );
3712
				break;
3713
			default:
3714
				$response = new WP_Error( 'unknown_handler', 'Unknown Handler', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unknown_handler'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3715
				break;
3716
		}
3717
3718
		if ( ! $response ) {
3719
			$response = new WP_Error( 'unknown_error', 'Unknown Error', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unknown_error'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3720
		}
3721
3722
		if ( is_wp_error( $response ) ) {
3723
			$status_code       = $response->get_error_data();
0 ignored issues
show
The method get_error_data() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3724
			$error             = $response->get_error_code();
0 ignored issues
show
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3725
			$error_description = $response->get_error_message();
0 ignored issues
show
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3726
3727
			if ( ! is_int( $status_code ) ) {
3728
				$status_code = 400;
3729
			}
3730
3731
			status_header( $status_code );
3732
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3733
		}
3734
3735
		status_header( 200 );
3736
		if ( true === $response ) {
3737
			exit;
3738
		}
3739
3740
		die( json_encode( (object) $response ) );
3741
	}
3742
3743
	/**
3744
	 * Uploads a file gotten from the global $_FILES.
3745
	 * If `$update_media_item` is true and `post_id` is defined
3746
	 * the attachment file of the media item (gotten through of the post_id)
3747
	 * will be updated instead of add a new one.
3748
	 *
3749
	 * @param  boolean $update_media_item - update media attachment
3750
	 * @return array - An array describing the uploadind files process
3751
	 */
3752
	function upload_handler( $update_media_item = false ) {
3753
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3754
			return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 405.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3755
		}
3756
3757
		$user = wp_authenticate( '', '' );
3758
		if ( ! $user || is_wp_error( $user ) ) {
3759
			return new WP_Error( 403, get_status_header_desc( 403 ), 403 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 403.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3760
		}
3761
3762
		wp_set_current_user( $user->ID );
3763
3764
		if ( ! current_user_can( 'upload_files' ) ) {
3765
			return new WP_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'cannot_upload_files'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3766
		}
3767
3768
		if ( empty( $_FILES ) ) {
3769
			return new WP_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'no_files_uploaded'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3770
		}
3771
3772
		foreach ( array_keys( $_FILES ) as $files_key ) {
3773
			if ( ! isset( $_POST[ "_jetpack_file_hmac_{$files_key}" ] ) ) {
3774
				return new WP_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'missing_hmac'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3775
			}
3776
		}
3777
3778
		$media_keys = array_keys( $_FILES['media'] );
3779
3780
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3781
		if ( ! $token || is_wp_error( $token ) ) {
3782
			return new WP_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unknown_token'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3783
		}
3784
3785
		$uploaded_files = array();
3786
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3787
		unset( $GLOBALS['post'] );
3788
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3789
			$file = array();
3790
			foreach ( $media_keys as $media_key ) {
3791
				$file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ];
3792
			}
3793
3794
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][ $index ] );
3795
3796
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3797
			if ( $hmac_provided !== $hmac_file ) {
3798
				$uploaded_files[ $index ] = (object) array(
3799
					'error'             => 'invalid_hmac',
3800
					'error_description' => 'The corresponding HMAC for this file does not match',
3801
				);
3802
				continue;
3803
			}
3804
3805
			$_FILES['.jetpack.upload.'] = $file;
3806
			$post_id                    = isset( $_POST['post_id'][ $index ] ) ? absint( $_POST['post_id'][ $index ] ) : 0;
3807
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3808
				$post_id = 0;
3809
			}
3810
3811
			if ( $update_media_item ) {
3812
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3813
					return new WP_Error( 'invalid_input', 'Media ID must be defined.', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3814
				}
3815
3816
				$media_array = $_FILES['media'];
3817
3818
				$file_array['name']     = $media_array['name'][0];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$file_array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $file_array = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
3819
				$file_array['type']     = $media_array['type'][0];
3820
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3821
				$file_array['error']    = $media_array['error'][0];
3822
				$file_array['size']     = $media_array['size'][0];
3823
3824
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3825
3826
				if ( is_wp_error( $edited_media_item ) ) {
3827
					return $edited_media_item;
3828
				}
3829
3830
				$response = (object) array(
3831
					'id'   => (string) $post_id,
3832
					'file' => (string) $edited_media_item->post_title,
3833
					'url'  => (string) wp_get_attachment_url( $post_id ),
3834
					'type' => (string) $edited_media_item->post_mime_type,
3835
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3836
				);
3837
3838
				return (array) array( $response );
3839
			}
3840
3841
			$attachment_id = media_handle_upload(
3842
				'.jetpack.upload.',
3843
				$post_id,
3844
				array(),
3845
				array(
3846
					'action' => 'jetpack_upload_file',
3847
				)
3848
			);
3849
3850
			if ( ! $attachment_id ) {
3851
				$uploaded_files[ $index ] = (object) array(
3852
					'error'             => 'unknown',
3853
					'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site',
3854
				);
3855
			} elseif ( is_wp_error( $attachment_id ) ) {
3856
				$uploaded_files[ $index ] = (object) array(
3857
					'error'             => 'attachment_' . $attachment_id->get_error_code(),
3858
					'error_description' => $attachment_id->get_error_message(),
3859
				);
3860
			} else {
3861
				$attachment               = get_post( $attachment_id );
3862
				$uploaded_files[ $index ] = (object) array(
3863
					'id'   => (string) $attachment_id,
3864
					'file' => $attachment->post_title,
3865
					'url'  => wp_get_attachment_url( $attachment_id ),
3866
					'type' => $attachment->post_mime_type,
3867
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3868
				);
3869
				// Zip files uploads are not supported unless they are done for installation purposed
3870
				// lets delete them in case something goes wrong in this whole process
3871
				if ( 'application/zip' === $attachment->post_mime_type ) {
3872
					// Schedule a cleanup for 2 hours from now in case of failed install.
3873
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3874
				}
3875
			}
3876
		}
3877
		if ( ! is_null( $global_post ) ) {
3878
			$GLOBALS['post'] = $global_post;
3879
		}
3880
3881
		return $uploaded_files;
3882
	}
3883
3884
	/**
3885
	 * Add help to the Jetpack page
3886
	 *
3887
	 * @since Jetpack (1.2.3)
3888
	 * @return false if not the Jetpack page
3889
	 */
3890
	function admin_help() {
3891
		$current_screen = get_current_screen();
3892
3893
		// Overview
3894
		$current_screen->add_help_tab(
3895
			array(
3896
				'id'      => 'home',
3897
				'title'   => __( 'Home', 'jetpack' ),
3898
				'content' =>
3899
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3900
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3901
					'<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>',
3902
			)
3903
		);
3904
3905
		// Screen Content
3906
		if ( current_user_can( 'manage_options' ) ) {
3907
			$current_screen->add_help_tab(
3908
				array(
3909
					'id'      => 'settings',
3910
					'title'   => __( 'Settings', 'jetpack' ),
3911
					'content' =>
3912
						'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3913
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3914
						'<ol>' .
3915
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3916
							'<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>' .
3917
						'</ol>' .
3918
						'<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>',
3919
				)
3920
			);
3921
		}
3922
3923
		// Help Sidebar
3924
		$support_url = Redirect::get_url( 'jetpack-support' );
3925
		$faq_url     = Redirect::get_url( 'jetpack-faq' );
3926
		$current_screen->set_help_sidebar(
3927
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3928
			'<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3929
			'<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3930
			'<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3931
		);
3932
	}
3933
3934
	function admin_menu_css() {
3935
		wp_enqueue_style( 'jetpack-icons' );
3936
	}
3937
3938
	function admin_menu_order() {
3939
		return true;
3940
	}
3941
3942
	function jetpack_menu_order( $menu_order ) {
3943
		$jp_menu_order = array();
3944
3945
		foreach ( $menu_order as $index => $item ) {
3946
			if ( $item != 'jetpack' ) {
3947
				$jp_menu_order[] = $item;
3948
			}
3949
3950
			if ( $index == 0 ) {
3951
				$jp_menu_order[] = 'jetpack';
3952
			}
3953
		}
3954
3955
		return $jp_menu_order;
3956
	}
3957
3958
	function admin_banner_styles() {
3959
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3960
3961 View Code Duplication
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3962
			wp_register_style(
3963
				'jetpack-dops-style',
3964
				plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3965
				array(),
3966
				JETPACK__VERSION
3967
			);
3968
		}
3969
3970
		wp_enqueue_style(
3971
			'jetpack',
3972
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3973
			array( 'jetpack-dops-style' ),
3974
			JETPACK__VERSION . '-20121016'
3975
		);
3976
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3977
		wp_style_add_data( 'jetpack', 'suffix', $min );
3978
	}
3979
3980
	function plugin_action_links( $actions ) {
3981
3982
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), 'Jetpack' ) );
3983
3984
		if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_active() || ( new Status() )->is_offline_mode() ) ) {
3985
			return array_merge(
3986
				$jetpack_home,
3987
				array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3988
				array( 'support' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack-debugger ' ), __( 'Support', 'jetpack' ) ) ),
3989
				$actions
3990
			);
3991
		}
3992
3993
		return array_merge( $jetpack_home, $actions );
3994
	}
3995
3996
	/**
3997
	 * Adds the deactivation warning modal if there are other active plugins using the connection
3998
	 *
3999
	 * @param string $hook The current admin page.
4000
	 *
4001
	 * @return void
4002
	 */
4003
	public function deactivate_dialog( $hook ) {
4004
		if (
4005
			'plugins.php' === $hook
4006
			&& self::is_active()
4007
		) {
4008
4009
			$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4010
4011
			if ( count( $active_plugins_using_connection ) > 1 ) {
4012
4013
				add_thickbox();
4014
4015
				wp_register_script(
4016
					'jp-tracks',
4017
					'//stats.wp.com/w.js',
4018
					array(),
4019
					gmdate( 'YW' ),
4020
					true
4021
				);
4022
4023
				wp_register_script(
4024
					'jp-tracks-functions',
4025
					plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ),
4026
					array( 'jp-tracks' ),
4027
					JETPACK__VERSION,
4028
					false
4029
				);
4030
4031
				wp_enqueue_script(
4032
					'jetpack-deactivate-dialog-js',
4033
					Assets::get_file_url_for_environment(
4034
						'_inc/build/jetpack-deactivate-dialog.min.js',
4035
						'_inc/jetpack-deactivate-dialog.js'
4036
					),
4037
					array( 'jquery', 'jp-tracks-functions' ),
4038
					JETPACK__VERSION,
4039
					true
4040
				);
4041
4042
				wp_localize_script(
4043
					'jetpack-deactivate-dialog-js',
4044
					'deactivate_dialog',
4045
					array(
4046
						'title'            => __( 'Deactivate Jetpack', 'jetpack' ),
4047
						'deactivate_label' => __( 'Disconnect and Deactivate', 'jetpack' ),
4048
						'tracksUserData'   => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
4049
					)
4050
				);
4051
4052
				add_action( 'admin_footer', array( $this, 'deactivate_dialog_content' ) );
4053
4054
				wp_enqueue_style( 'jetpack-deactivate-dialog', plugins_url( 'css/jetpack-deactivate-dialog.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
4055
			}
4056
		}
4057
	}
4058
4059
	/**
4060
	 * Outputs the content of the deactivation modal
4061
	 *
4062
	 * @return void
4063
	 */
4064
	public function deactivate_dialog_content() {
4065
		$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4066
		unset( $active_plugins_using_connection['jetpack'] );
4067
		$this->load_view( 'admin/deactivation-dialog.php', $active_plugins_using_connection );
0 ignored issues
show
It seems like $active_plugins_using_connection defined by \Automattic\Jetpack\Conn...ugin_Storage::get_all() on line 4065 can also be of type object<WP_Error>; however, Jetpack::load_view() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
4068
	}
4069
4070
	/**
4071
	 * Filters the login URL to include the registration flow in case the user isn't logged in.
4072
	 *
4073
	 * @param string $login_url The wp-login URL.
4074
	 * @param string $redirect  URL to redirect users after logging in.
4075
	 * @since Jetpack 8.4
4076
	 * @return string
4077
	 */
4078
	public function login_url( $login_url, $redirect ) {
4079
		parse_str( wp_parse_url( $redirect, PHP_URL_QUERY ), $redirect_parts );
4080
		if ( ! empty( $redirect_parts[ self::$jetpack_redirect_login ] ) ) {
4081
			$login_url = add_query_arg( self::$jetpack_redirect_login, 'true', $login_url );
4082
		}
4083
		return $login_url;
4084
	}
4085
4086
	/**
4087
	 * Redirects non-authenticated users to authenticate with Calypso if redirect flag is set.
4088
	 *
4089
	 * @since Jetpack 8.4
4090
	 */
4091
	public function login_init() {
4092
		// phpcs:ignore WordPress.Security.NonceVerification
4093
		if ( ! empty( $_GET[ self::$jetpack_redirect_login ] ) ) {
4094
			add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4095
			wp_safe_redirect(
4096
				add_query_arg(
4097
					array(
4098
						'forceInstall' => 1,
4099
						'url'          => rawurlencode( get_site_url() ),
4100
					),
4101
					// @todo provide way to go to specific calypso env.
4102
					self::get_calypso_host() . 'jetpack/connect'
4103
				)
4104
			);
4105
			exit;
4106
		}
4107
	}
4108
4109
	/*
4110
	 * Registration flow:
4111
	 * 1 - ::admin_page_load() action=register
4112
	 * 2 - ::try_registration()
4113
	 * 3 - ::register()
4114
	 *     - Creates jetpack_register option containing two secrets and a timestamp
4115
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4116
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4117
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
4118
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4119
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4120
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4121
	 *       jetpack_id, jetpack_secret, jetpack_public
4122
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4123
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4124
	 * 5 - user logs in with WP.com account
4125
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4126
	 *		- Manager::authorize()
4127
	 *		- Manager::get_token()
4128
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
4129
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4130
	 *			- which responds with access_token, token_type, scope
4131
	 *		- Manager::authorize() stores jetpack_options: user_token => access_token.$user_id
4132
	 *		- Jetpack::activate_default_modules()
4133
	 *     		- Deactivates deprecated plugins
4134
	 *     		- Activates all default modules
4135
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4136
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4137
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4138
	 *     Done!
4139
	 */
4140
4141
	/**
4142
	 * Handles the page load events for the Jetpack admin page
4143
	 */
4144
	function admin_page_load() {
4145
		$error = false;
4146
4147
		// Make sure we have the right body class to hook stylings for subpages off of.
4148
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ), 20 );
4149
4150
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
4151
			// Should only be used in intermediate redirects to preserve state across redirects
4152
			self::restate();
4153
		}
4154
4155
		if ( isset( $_GET['connect_url_redirect'] ) ) {
4156
			// @todo: Add validation against a known allowed list.
4157
			$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4158
			// User clicked in the iframe to link their accounts
4159
			if ( ! self::is_user_connected() ) {
4160
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4161
4162
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4163
				$connect_url = $this->build_connect_url( true, $redirect, $from );
4164
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4165
4166
				if ( isset( $_GET['notes_iframe'] ) ) {
4167
					$connect_url .= '&notes_iframe';
4168
				}
4169
				wp_redirect( $connect_url );
4170
				exit;
4171
			} else {
4172
				if ( ! isset( $_GET['calypso_env'] ) ) {
4173
					self::state( 'message', 'already_authorized' );
4174
					wp_safe_redirect( self::admin_url() );
4175
					exit;
4176
				} else {
4177
					$connect_url  = $this->build_connect_url( true, false, $from );
4178
					$connect_url .= '&already_authorized=true';
4179
					wp_redirect( $connect_url );
4180
					exit;
4181
				}
4182
			}
4183
		}
4184
4185
		if ( isset( $_GET['action'] ) ) {
4186
			switch ( $_GET['action'] ) {
4187
				case 'authorize':
4188
					if ( self::is_active() && self::is_user_connected() ) {
4189
						self::state( 'message', 'already_authorized' );
4190
						wp_safe_redirect( self::admin_url() );
4191
						exit;
4192
					}
4193
					self::log( 'authorize' );
4194
					$client_server = new Jetpack_Client_Server();
4195
					$client_server->client_authorize();
4196
					exit;
4197
				case 'register':
4198
					if ( ! current_user_can( 'jetpack_connect' ) ) {
4199
						$error = 'cheatin';
4200
						break;
4201
					}
4202
					check_admin_referer( 'jetpack-register' );
4203
					self::log( 'register' );
4204
					self::maybe_set_version_option();
4205
					$registered = self::try_registration();
4206 View Code Duplication
					if ( is_wp_error( $registered ) ) {
4207
						$error = $registered->get_error_code();
0 ignored issues
show
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
4208
						self::state( 'error', $error );
4209
						self::state( 'error', $registered->get_error_message() );
0 ignored issues
show
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
4210
4211
						/**
4212
						 * Jetpack registration Error.
4213
						 *
4214
						 * @since 7.5.0
4215
						 *
4216
						 * @param string|int $error The error code.
4217
						 * @param \WP_Error $registered The error object.
4218
						 */
4219
						do_action( 'jetpack_connection_register_fail', $error, $registered );
4220
						break;
4221
					}
4222
4223
					$from     = isset( $_GET['from'] ) ? $_GET['from'] : false;
4224
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4225
4226
					/**
4227
					 * Jetpack registration Success.
4228
					 *
4229
					 * @since 7.5.0
4230
					 *
4231
					 * @param string $from 'from' GET parameter;
4232
					 */
4233
					do_action( 'jetpack_connection_register_success', $from );
4234
4235
					$url = $this->build_connect_url( true, $redirect, $from );
4236
4237
					if ( ! empty( $_GET['onboarding'] ) ) {
4238
						$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4239
					}
4240
4241
					if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4242
						$url = add_query_arg( 'auth_approved', 'true', $url );
4243
					}
4244
4245
					wp_redirect( $url );
4246
					exit;
4247
				case 'activate':
4248
					if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4249
						$error = 'cheatin';
4250
						break;
4251
					}
4252
4253
					$module = stripslashes( $_GET['module'] );
4254
					check_admin_referer( "jetpack_activate-$module" );
4255
					self::log( 'activate', $module );
4256
					if ( ! self::activate_module( $module ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::activate_module($module) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
4257
						self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4258
					}
4259
					// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4260
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4261
					exit;
4262
				case 'activate_default_modules':
4263
					check_admin_referer( 'activate_default_modules' );
4264
					self::log( 'activate_default_modules' );
4265
					self::restate();
4266
					$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4267
					$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4268
					$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4269
					self::activate_default_modules( $min_version, $max_version, $other_modules );
4270
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4271
					exit;
4272
				case 'disconnect':
4273
					if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4274
						$error = 'cheatin';
4275
						break;
4276
					}
4277
4278
					check_admin_referer( 'jetpack-disconnect' );
4279
					self::log( 'disconnect' );
4280
					self::disconnect();
4281
					wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4282
					exit;
4283
				case 'reconnect':
4284
					if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4285
						$error = 'cheatin';
4286
						break;
4287
					}
4288
4289
					check_admin_referer( 'jetpack-reconnect' );
4290
					self::log( 'reconnect' );
4291
					$this->disconnect();
4292
					wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4293
					exit;
4294 View Code Duplication
				case 'deactivate':
4295
					if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4296
						$error = 'cheatin';
4297
						break;
4298
					}
4299
4300
					$modules = stripslashes( $_GET['module'] );
4301
					check_admin_referer( "jetpack_deactivate-$modules" );
4302
					foreach ( explode( ',', $modules ) as $module ) {
4303
						self::log( 'deactivate', $module );
4304
						self::deactivate_module( $module );
4305
						self::state( 'message', 'module_deactivated' );
4306
					}
4307
					self::state( 'module', $modules );
4308
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4309
					exit;
4310
				case 'unlink':
4311
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4312
					check_admin_referer( 'jetpack-unlink' );
4313
					self::log( 'unlink' );
4314
					Connection_Manager::disconnect_user();
4315
					self::state( 'message', 'unlinked' );
4316
					if ( 'sub-unlink' == $redirect ) {
4317
						wp_safe_redirect( admin_url() );
4318
					} else {
4319
						wp_safe_redirect( self::admin_url( array( 'page' => $redirect ) ) );
4320
					}
4321
					exit;
4322
				case 'onboard':
4323
					if ( ! current_user_can( 'manage_options' ) ) {
4324
						wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4325
					} else {
4326
						self::create_onboarding_token();
4327
						$url = $this->build_connect_url( true );
4328
4329
						if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4330
							$url = add_query_arg( 'onboarding', $token, $url );
4331
						}
4332
4333
						$calypso_env = $this->get_calypso_env();
4334
						if ( ! empty( $calypso_env ) ) {
4335
							$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4336
						}
4337
4338
						wp_redirect( $url );
4339
						exit;
4340
					}
4341
					exit;
4342
				default:
4343
					/**
4344
					 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4345
					 *
4346
					 * @since 2.6.0
4347
					 *
4348
					 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4349
					 */
4350
					do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4351
			}
4352
		}
4353
4354
		if ( ! $error = $error ? $error : self::state( 'error' ) ) {
4355
			self::activate_new_modules( true );
4356
		}
4357
4358
		$message_code = self::state( 'message' );
4359
		if ( self::state( 'optin-manage' ) ) {
4360
			$activated_manage = $message_code;
4361
			$message_code     = 'jetpack-manage';
4362
		}
4363
4364
		switch ( $message_code ) {
4365
			case 'jetpack-manage':
4366
				$sites_url = esc_url( Redirect::get_url( 'calypso-sites' ) );
4367
				// translators: %s is the URL to the "Sites" panel on wordpress.com.
4368
				$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>';
4369
				if ( $activated_manage ) {
0 ignored issues
show
The variable $activated_manage does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4370
					$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack' ) . '</strong>';
4371
				}
4372
				break;
4373
4374
		}
4375
4376
		$deactivated_plugins = self::state( 'deactivated_plugins' );
4377
4378
		if ( ! empty( $deactivated_plugins ) ) {
4379
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4380
			$deactivated_titles  = array();
4381
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4382
				if ( ! isset( $this->plugins_to_deactivate[ $deactivated_plugin ] ) ) {
4383
					continue;
4384
				}
4385
4386
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[ $deactivated_plugin ][1] ) . '</strong>';
4387
			}
4388
4389
			if ( $deactivated_titles ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated_titles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
4390
				if ( $this->message ) {
4391
					$this->message .= "<br /><br />\n";
4392
				}
4393
4394
				$this->message .= wp_sprintf(
4395
					_n(
4396
						'Jetpack contains the most recent version of the old %l plugin.',
4397
						'Jetpack contains the most recent versions of the old %l plugins.',
4398
						count( $deactivated_titles ),
4399
						'jetpack'
4400
					),
4401
					$deactivated_titles
4402
				);
4403
4404
				$this->message .= "<br />\n";
4405
4406
				$this->message .= _n(
4407
					'The old version has been deactivated and can be removed from your site.',
4408
					'The old versions have been deactivated and can be removed from your site.',
4409
					count( $deactivated_titles ),
4410
					'jetpack'
4411
				);
4412
			}
4413
		}
4414
4415
		$this->privacy_checks = self::state( 'privacy_checks' );
4416
4417
		if ( $this->message || $this->error || $this->privacy_checks ) {
4418
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4419
		}
4420
4421
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4422
	}
4423
4424
	function admin_notices() {
4425
4426
		if ( $this->error ) {
4427
			?>
4428
<div id="message" class="jetpack-message jetpack-err">
4429
	<div class="squeezer">
4430
		<h2>
4431
			<?php
4432
			echo wp_kses(
4433
				$this->error,
4434
				array(
4435
					'a'      => array( 'href' => array() ),
4436
					'small'  => true,
4437
					'code'   => true,
4438
					'strong' => true,
4439
					'br'     => true,
4440
					'b'      => true,
4441
				)
4442
			);
4443
			?>
4444
			</h2>
4445
			<?php	if ( $desc = self::state( 'error_description' ) ) : ?>
4446
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4447
<?php	endif; ?>
4448
	</div>
4449
</div>
4450
			<?php
4451
		}
4452
4453
		if ( $this->message ) {
4454
			?>
4455
<div id="message" class="jetpack-message">
4456
	<div class="squeezer">
4457
		<h2>
4458
			<?php
4459
			echo wp_kses(
4460
				$this->message,
4461
				array(
4462
					'strong' => array(),
4463
					'a'      => array( 'href' => true ),
4464
					'br'     => true,
4465
				)
4466
			);
4467
			?>
4468
			</h2>
4469
	</div>
4470
</div>
4471
			<?php
4472
		}
4473
4474
		if ( $this->privacy_checks ) :
4475
			$module_names = $module_slugs = array();
4476
4477
			$privacy_checks = explode( ',', $this->privacy_checks );
4478
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4479
			foreach ( $privacy_checks as $module_slug ) {
4480
				$module = self::get_module( $module_slug );
4481
				if ( ! $module ) {
4482
					continue;
4483
				}
4484
4485
				$module_slugs[] = $module_slug;
4486
				$module_names[] = "<strong>{$module['name']}</strong>";
4487
			}
4488
4489
			$module_slugs = join( ',', $module_slugs );
4490
			?>
4491
<div id="message" class="jetpack-message jetpack-err">
4492
	<div class="squeezer">
4493
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4494
		<p>
4495
			<?php
4496
			echo wp_kses(
4497
				wptexturize(
4498
					wp_sprintf(
4499
						_nx(
4500
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4501
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4502
							count( $privacy_checks ),
4503
							'%l = list of Jetpack module/feature names',
4504
							'jetpack'
4505
						),
4506
						$module_names
4507
					)
4508
				),
4509
				array( 'strong' => true )
4510
			);
4511
4512
			echo "\n<br />\n";
4513
4514
			echo wp_kses(
4515
				sprintf(
4516
					_nx(
4517
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4518
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4519
						count( $privacy_checks ),
4520
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4521
						'jetpack'
4522
					),
4523
					wp_nonce_url(
4524
						self::admin_url(
4525
							array(
4526
								'page'   => 'jetpack',
4527
								'action' => 'deactivate',
4528
								'module' => urlencode( $module_slugs ),
4529
							)
4530
						),
4531
						"jetpack_deactivate-$module_slugs"
4532
					),
4533
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4534
				),
4535
				array(
4536
					'a' => array(
4537
						'href'  => true,
4538
						'title' => true,
4539
					),
4540
				)
4541
			);
4542
			?>
4543
		</p>
4544
	</div>
4545
</div>
4546
			<?php
4547
endif;
4548
	}
4549
4550
	/**
4551
	 * We can't always respond to a signed XML-RPC request with a
4552
	 * helpful error message. In some circumstances, doing so could
4553
	 * leak information.
4554
	 *
4555
	 * Instead, track that the error occurred via a Jetpack_Option,
4556
	 * and send that data back in the heartbeat.
4557
	 * All this does is increment a number, but it's enough to find
4558
	 * trends.
4559
	 *
4560
	 * @param WP_Error $xmlrpc_error The error produced during
4561
	 *                               signature validation.
4562
	 */
4563
	function track_xmlrpc_error( $xmlrpc_error ) {
4564
		$code = is_wp_error( $xmlrpc_error )
4565
			? $xmlrpc_error->get_error_code()
0 ignored issues
show
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
4566
			: 'should-not-happen';
4567
4568
		$xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4569
		if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4570
			// No need to update the option if we already have
4571
			// this code stored.
4572
			return;
4573
		}
4574
		$xmlrpc_errors[ $code ] = true;
4575
4576
		Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
0 ignored issues
show
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4577
	}
4578
4579
	/**
4580
	 * Initialize the jetpack stats instance only when needed
4581
	 *
4582
	 * @return void
4583
	 */
4584
	private function initialize_stats() {
4585
		if ( is_null( $this->a8c_mc_stats_instance ) ) {
4586
			$this->a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4587
		}
4588
	}
4589
4590
	/**
4591
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4592
	 */
4593
	function stat( $group, $detail ) {
4594
		$this->initialize_stats();
4595
		$this->a8c_mc_stats_instance->add( $group, $detail );
4596
4597
		// Keep a local copy for backward compatibility (there are some direct checks on this).
4598
		$this->stats = $this->a8c_mc_stats_instance->get_current_stats();
4599
	}
4600
4601
	/**
4602
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4603
	 */
4604
	function do_stats( $method = '' ) {
4605
		$this->initialize_stats();
4606
		if ( 'server_side' === $method ) {
4607
			$this->a8c_mc_stats_instance->do_server_side_stats();
4608
		} else {
4609
			$this->a8c_mc_stats_instance->do_stats();
4610
		}
4611
4612
		// Keep a local copy for backward compatibility (there are some direct checks on this).
4613
		$this->stats = array();
4614
	}
4615
4616
	/**
4617
	 * Runs stats code for a one-off, server-side.
4618
	 *
4619
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4620
	 *
4621
	 * @return bool If it worked.
4622
	 */
4623
	static function do_server_side_stat( $args ) {
4624
		$url                   = self::build_stats_url( $args );
4625
		$a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4626
		return $a8c_mc_stats_instance->do_server_side_stat( $url );
4627
	}
4628
4629
	/**
4630
	 * Builds the stats url.
4631
	 *
4632
	 * @param $args array|string The arguments to append to the URL.
4633
	 *
4634
	 * @return string The URL to be pinged.
4635
	 */
4636
	static function build_stats_url( $args ) {
4637
4638
		$a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4639
		return $a8c_mc_stats_instance->build_stats_url( $args );
4640
4641
	}
4642
4643
	/**
4644
	 * Get the role of the current user.
4645
	 *
4646
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_current_user_to_role() instead.
4647
	 *
4648
	 * @access public
4649
	 * @static
4650
	 *
4651
	 * @return string|boolean Current user's role, false if not enough capabilities for any of the roles.
4652
	 */
4653
	public static function translate_current_user_to_role() {
4654
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4655
4656
		$roles = new Roles();
4657
		return $roles->translate_current_user_to_role();
4658
	}
4659
4660
	/**
4661
	 * Get the role of a particular user.
4662
	 *
4663
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_user_to_role() instead.
4664
	 *
4665
	 * @access public
4666
	 * @static
4667
	 *
4668
	 * @param \WP_User $user User object.
4669
	 * @return string|boolean User's role, false if not enough capabilities for any of the roles.
4670
	 */
4671
	public static function translate_user_to_role( $user ) {
4672
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4673
4674
		$roles = new Roles();
4675
		return $roles->translate_user_to_role( $user );
4676
	}
4677
4678
	/**
4679
	 * Get the minimum capability for a role.
4680
	 *
4681
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_role_to_cap() instead.
4682
	 *
4683
	 * @access public
4684
	 * @static
4685
	 *
4686
	 * @param string $role Role name.
4687
	 * @return string|boolean Capability, false if role isn't mapped to any capabilities.
4688
	 */
4689
	public static function translate_role_to_cap( $role ) {
4690
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4691
4692
		$roles = new Roles();
4693
		return $roles->translate_role_to_cap( $role );
4694
	}
4695
4696
	/**
4697
	 * Sign a user role with the master access token.
4698
	 * If not specified, will default to the current user.
4699
	 *
4700
	 * @deprecated since 7.7
4701
	 * @see Automattic\Jetpack\Connection\Manager::sign_role()
4702
	 *
4703
	 * @access public
4704
	 * @static
4705
	 *
4706
	 * @param string $role    User role.
4707
	 * @param int    $user_id ID of the user.
0 ignored issues
show
Should the type for parameter $user_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
4708
	 * @return string Signed user role.
4709
	 */
4710
	public static function sign_role( $role, $user_id = null ) {
4711
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::sign_role' );
4712
		return self::connection()->sign_role( $role, $user_id );
4713
	}
4714
4715
	/**
4716
	 * Builds a URL to the Jetpack connection auth page
4717
	 *
4718
	 * @since 3.9.5
4719
	 *
4720
	 * @param bool        $raw If true, URL will not be escaped.
4721
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4722
	 *                              If string, will be a custom redirect.
4723
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4724
	 * @param bool        $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4725
	 *
4726
	 * @return string Connect URL
4727
	 */
4728
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4729
		$site_id    = Jetpack_Options::get_option( 'id' );
4730
		$blog_token = Jetpack_Data::get_access_token();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
4731
4732
		if ( $register || ! $blog_token || ! $site_id ) {
4733
			$url = self::nonce_url_no_esc( self::admin_url( 'action=register' ), 'jetpack-register' );
4734
4735
			if ( ! empty( $redirect ) ) {
4736
				$url = add_query_arg(
4737
					'redirect',
4738
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4739
					$url
4740
				);
4741
			}
4742
4743
			if ( is_network_admin() ) {
4744
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4745
			}
4746
4747
			$calypso_env = self::get_calypso_env();
4748
4749
			if ( ! empty( $calypso_env ) ) {
4750
				$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4751
			}
4752
		} else {
4753
4754
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4755
			// because otherwise this logic can get us in to a loop.
4756
			$last_connect_url_check = (int) Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' );
4757
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4758
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4759
4760
				$response = Client::wpcom_json_api_request_as_blog(
4761
					sprintf( '/sites/%d', $site_id ) . '?force=wpcom',
4762
					'1.1'
4763
				);
4764
4765
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4766
4767
					// Generating a register URL instead to refresh the existing token
4768
					return $this->build_connect_url( $raw, $redirect, $from, true );
4769
				}
4770
			}
4771
4772
			$url = $this->build_authorize_url( $redirect );
0 ignored issues
show
It seems like $redirect defined by parameter $redirect on line 4728 can also be of type string; however, Jetpack::build_authorize_url() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4773
		}
4774
4775
		if ( $from ) {
4776
			$url = add_query_arg( 'from', $from, $url );
4777
		}
4778
4779
		$url = $raw ? esc_url_raw( $url ) : esc_url( $url );
4780
		/**
4781
		 * Filter the URL used when connecting a user to a WordPress.com account.
4782
		 *
4783
		 * @since 8.1.0
4784
		 *
4785
		 * @param string $url Connection URL.
4786
		 * @param bool   $raw If true, URL will not be escaped.
4787
		 */
4788
		return apply_filters( 'jetpack_build_connection_url', $url, $raw );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $raw.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4789
	}
4790
4791
	public static function build_authorize_url( $redirect = false, $iframe = false ) {
4792
4793
		add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4794
		add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4795
		add_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4796
4797
		if ( $iframe ) {
4798
			add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4799
		}
4800
4801
		$c8n = self::connection();
4802
		$url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
0 ignored issues
show
$redirect is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4803
4804
		remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4805
		remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4806
		remove_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4807
4808
		if ( $iframe ) {
4809
			remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4810
		}
4811
4812
		/**
4813
		 * Filter the URL used when authorizing a user to a WordPress.com account.
4814
		 *
4815
		 * @since 8.9.0
4816
		 *
4817
		 * @param string $url Connection URL.
4818
		 */
4819
		return apply_filters( 'jetpack_build_authorize_url', $url );
4820
	}
4821
4822
	/**
4823
	 * Filters the connection URL parameter array.
4824
	 *
4825
	 * @param array $args default URL parameters used by the package.
4826
	 * @return array the modified URL arguments array.
4827
	 */
4828
	public static function filter_connect_request_body( $args ) {
4829
		if (
4830
			Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4831
			&& include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4832
		) {
4833
			$gp_locale      = GP_Locales::by_field( 'wp_locale', get_locale() );
4834
			$args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4835
				? $gp_locale->slug
4836
				: '';
4837
		}
4838
4839
		$tracking        = new Tracking();
4840
		$tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4841
4842
		$args = array_merge(
4843
			$args,
4844
			array(
4845
				'_ui' => $tracks_identity['_ui'],
4846
				'_ut' => $tracks_identity['_ut'],
4847
			)
4848
		);
4849
4850
		$calypso_env = self::get_calypso_env();
4851
4852
		if ( ! empty( $calypso_env ) ) {
4853
			$args['calypso_env'] = $calypso_env;
4854
		}
4855
4856
		return $args;
4857
	}
4858
4859
	/**
4860
	 * Filters the URL that will process the connection data. It can be different from the URL
4861
	 * that we send the user to after everything is done.
4862
	 *
4863
	 * @param String $processing_url the default redirect URL used by the package.
4864
	 * @return String the modified URL.
4865
	 */
4866
	public static function filter_connect_processing_url( $processing_url ) {
4867
		$processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4868
		return $processing_url;
4869
	}
4870
4871
	/**
4872
	 * Filters the redirection URL that is used for connect requests. The redirect
4873
	 * URL should return the user back to the Jetpack console.
4874
	 *
4875
	 * @param String $redirect the default redirect URL used by the package.
4876
	 * @return String the modified URL.
4877
	 */
4878
	public static function filter_connect_redirect_url( $redirect ) {
4879
		$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4880
		$redirect           = $redirect
4881
			? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4882
			: $jetpack_admin_page;
4883
4884
		if ( isset( $_REQUEST['is_multisite'] ) ) {
4885
			$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4886
		}
4887
4888
		return $redirect;
4889
	}
4890
4891
	/**
4892
	 * This action fires at the beginning of the Manager::authorize method.
4893
	 */
4894
	public static function authorize_starting() {
4895
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
4896
		// Checking if site has been active/connected previously before recording unique connection.
4897
		if ( ! $jetpack_unique_connection ) {
4898
			// jetpack_unique_connection option has never been set.
4899
			$jetpack_unique_connection = array(
4900
				'connected'    => 0,
4901
				'disconnected' => 0,
4902
				'version'      => '3.6.1',
4903
			);
4904
4905
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
4906
4907
			// Track unique connection.
4908
			$jetpack = self::init();
4909
4910
			$jetpack->stat( 'connections', 'unique-connection' );
4911
			$jetpack->do_stats( 'server_side' );
4912
		}
4913
4914
		// Increment number of times connected.
4915
		$jetpack_unique_connection['connected'] += 1;
4916
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
4917
	}
4918
4919
	/**
4920
	 * This action fires at the end of the Manager::authorize method when a secondary user is
4921
	 * linked.
4922
	 */
4923
	public static function authorize_ending_linked() {
4924
		// Don't activate anything since we are just connecting a user.
4925
		self::state( 'message', 'linked' );
4926
	}
4927
4928
	/**
4929
	 * This action fires at the end of the Manager::authorize method when the master user is
4930
	 * authorized.
4931
	 *
4932
	 * @param array $data The request data.
4933
	 */
4934
	public static function authorize_ending_authorized( $data ) {
4935
		// If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4936
		self::invalidate_onboarding_token();
4937
4938
		// If redirect_uri is SSO, ensure SSO module is enabled.
4939
		parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
4940
4941
		/** This filter is documented in class.jetpack-cli.php */
4942
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
4943
4944
		$activate_sso = (
4945
			isset( $redirect_options['action'] ) &&
4946
			'jetpack-sso' === $redirect_options['action'] &&
4947
			$jetpack_start_enable_sso
4948
		);
4949
4950
		$do_redirect_on_error = ( 'client' === $data['auth_type'] );
4951
4952
		self::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
4953
	}
4954
4955
	/**
4956
	 * This action fires at the end of the REST_Connector connection_reconnect method when the
4957
	 * reconnect process is completed.
4958
	 * Note that this currently only happens when we don't need the user to re-authorize
4959
	 * their WP.com account, eg in cases where we are restoring a connection with
4960
	 * unhealthy blog token.
4961
	 */
4962
	public static function reconnection_completed() {
4963
		self::state( 'message', 'reconnection_completed' );
4964
	}
4965
4966
	/**
4967
	 * Get our assumed site creation date.
4968
	 * Calculated based on the earlier date of either:
4969
	 * - Earliest admin user registration date.
4970
	 * - Earliest date of post of any post type.
4971
	 *
4972
	 * @since 7.2.0
4973
	 * @deprecated since 7.8.0
4974
	 *
4975
	 * @return string Assumed site creation date and time.
4976
	 */
4977
	public static function get_assumed_site_creation_date() {
4978
		_deprecated_function( __METHOD__, 'jetpack-7.8', 'Automattic\\Jetpack\\Connection\\Manager' );
4979
		return self::connection()->get_assumed_site_creation_date();
4980
	}
4981
4982 View Code Duplication
	public static function apply_activation_source_to_args( &$args ) {
4983
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
4984
4985
		if ( $activation_source_name ) {
4986
			$args['_as'] = urlencode( $activation_source_name );
4987
		}
4988
4989
		if ( $activation_source_keyword ) {
4990
			$args['_ak'] = urlencode( $activation_source_keyword );
4991
		}
4992
	}
4993
4994
	function build_reconnect_url( $raw = false ) {
4995
		$url = wp_nonce_url( self::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4996
		return $raw ? $url : esc_url( $url );
4997
	}
4998
4999
	public static function admin_url( $args = null ) {
5000
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
0 ignored issues
show
array('page' => 'jetpack') is of type array<string,string,{"page":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5001
		$url  = add_query_arg( $args, admin_url( 'admin.php' ) );
5002
		return $url;
5003
	}
5004
5005
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
5006
		$actionurl = str_replace( '&amp;', '&', $actionurl );
5007
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
5008
	}
5009
5010
	function dismiss_jetpack_notice() {
5011
5012
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
5013
			return;
5014
		}
5015
5016
		switch ( $_GET['jetpack-notice'] ) {
5017
			case 'dismiss':
5018
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
5019
5020
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
5021
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
5022
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
5023
				}
5024
				break;
5025
		}
5026
	}
5027
5028
	public static function sort_modules( $a, $b ) {
5029
		if ( $a['sort'] == $b['sort'] ) {
5030
			return 0;
5031
		}
5032
5033
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5034
	}
5035
5036
	function ajax_recheck_ssl() {
5037
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
5038
		$result = self::permit_ssl( true );
5039
		wp_send_json(
5040
			array(
5041
				'enabled' => $result,
5042
				'message' => get_transient( 'jetpack_https_test_message' ),
5043
			)
5044
		);
5045
	}
5046
5047
	/* Client API */
5048
5049
	/**
5050
	 * Returns the requested Jetpack API URL
5051
	 *
5052
	 * @deprecated since 7.7
5053
	 * @return string
5054
	 */
5055
	public static function api_url( $relative_url ) {
5056
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::api_url' );
5057
		$connection = self::connection();
5058
		return $connection->api_url( $relative_url );
5059
	}
5060
5061
	/**
5062
	 * @deprecated 8.0
5063
	 *
5064
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requests.
5065
	 * But we no longer fix "bad hosts" anyway, outbound HTTPS is required for Jetpack to function.
5066
	 */
5067
	public static function fix_url_for_bad_hosts( $url ) {
5068
		_deprecated_function( __METHOD__, 'jetpack-8.0' );
5069
		return $url;
5070
	}
5071
5072
	public static function verify_onboarding_token( $token_data, $token, $request_data ) {
5073
		// Default to a blog token.
5074
		$token_type = 'blog';
5075
5076
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5077
		if ( isset( $request_data ) || ! empty( $_GET['onboarding'] ) ) {
5078
			if ( ! empty( $_GET['onboarding'] ) ) {
5079
				$jpo = $_GET;
5080
			} else {
5081
				$jpo = json_decode( $request_data, true );
5082
			}
5083
5084
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5085
			$jpo_user  = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5086
5087
			if (
5088
				isset( $jpo_user )
5089
				&& isset( $jpo_token )
5090
				&& is_email( $jpo_user )
5091
				&& ctype_alnum( $jpo_token )
5092
				&& isset( $_GET['rest_route'] )
5093
				&& self::validate_onboarding_token_action(
5094
					$jpo_token,
5095
					$_GET['rest_route']
5096
				)
5097
			) {
5098
				$jp_user = get_user_by( 'email', $jpo_user );
5099
				if ( is_a( $jp_user, 'WP_User' ) ) {
5100
					wp_set_current_user( $jp_user->ID );
5101
					$user_can = is_multisite()
5102
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5103
						: current_user_can( 'manage_options' );
5104
					if ( $user_can ) {
5105
						$token_type              = 'user';
5106
						$token->external_user_id = $jp_user->ID;
5107
					}
5108
				}
5109
			}
5110
5111
			$token_data['type']    = $token_type;
5112
			$token_data['user_id'] = $token->external_user_id;
5113
		}
5114
5115
		return $token_data;
5116
	}
5117
5118
	/**
5119
	 * Create a random secret for validating onboarding payload
5120
	 *
5121
	 * @return string Secret token
5122
	 */
5123
	public static function create_onboarding_token() {
5124
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
5125
			$token = wp_generate_password( 32, false );
5126
			Jetpack_Options::update_option( 'onboarding', $token );
5127
		}
5128
5129
		return $token;
5130
	}
5131
5132
	/**
5133
	 * Remove the onboarding token
5134
	 *
5135
	 * @return bool True on success, false on failure
5136
	 */
5137
	public static function invalidate_onboarding_token() {
5138
		return Jetpack_Options::delete_option( 'onboarding' );
5139
	}
5140
5141
	/**
5142
	 * Validate an onboarding token for a specific action
5143
	 *
5144
	 * @return boolean True if token/action pair is accepted, false if not
5145
	 */
5146
	public static function validate_onboarding_token_action( $token, $action ) {
5147
		// Compare tokens, bail if tokens do not match
5148
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5149
			return false;
5150
		}
5151
5152
		// List of valid actions we can take
5153
		$valid_actions = array(
5154
			'/jetpack/v4/settings',
5155
		);
5156
5157
		// Only allow valid actions.
5158
		if ( ! in_array( $action, $valid_actions ) ) {
5159
			return false;
5160
		}
5161
5162
		return true;
5163
	}
5164
5165
	/**
5166
	 * Checks to see if the URL is using SSL to connect with Jetpack
5167
	 *
5168
	 * @since 2.3.3
5169
	 * @return boolean
5170
	 */
5171
	public static function permit_ssl( $force_recheck = false ) {
5172
		// Do some fancy tests to see if ssl is being supported
5173
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5174
			$message = '';
5175
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5176
				$ssl = 0;
5177
			} else {
5178
				$ssl = 1;
5179
5180
				if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5181
					$ssl     = 0;
5182
					$message = __( 'WordPress reports no SSL support', 'jetpack' );
5183
				} else {
5184
					$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5185
					if ( is_wp_error( $response ) ) {
5186
						$ssl     = 0;
5187
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
5188
					} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5189
						$ssl     = 0;
5190
						$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5191
					}
5192
				}
5193
			}
5194
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5195
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5196
		}
5197
5198
		return (bool) $ssl;
5199
	}
5200
5201
	/*
5202
	 * Displays an admin_notice, alerting the user that outbound SSL isn't working.
5203
	 */
5204
	public function alert_auto_ssl_fail() {
5205
		if ( ! current_user_can( 'manage_options' ) ) {
5206
			return;
5207
		}
5208
5209
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5210
		?>
5211
5212
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5213
			<div class="jp-banner__content">
5214
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5215
				<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>
5216
				<p>
5217
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5218
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5219
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5220
				</p>
5221
				<p>
5222
					<?php
5223
					printf(
5224
						__( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
5225
						esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ),
5226
						esc_url( Redirect::get_url( 'jetpack-support-getting-started-troubleshooting-tips' ) )
5227
					);
5228
					?>
5229
				</p>
5230
			</div>
5231
		</div>
5232
		<style>
5233
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5234
		</style>
5235
		<script type="text/javascript">
5236
			jQuery( document ).ready( function( $ ) {
5237
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5238
					var $this = $( this );
5239
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5240
					$( '#jetpack-recheck-ssl-output' ).html( '' );
5241
					e.preventDefault();
5242
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5243
					$.post( ajaxurl, data )
5244
					  .done( function( response ) {
5245
						  if ( response.enabled ) {
5246
							  $( '#jetpack-ssl-warning' ).hide();
5247
						  } else {
5248
							  this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5249
							  $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5250
						  }
5251
					  }.bind( $this ) );
5252
				} );
5253
			} );
5254
		</script>
5255
5256
		<?php
5257
	}
5258
5259
	/**
5260
	 * Returns the Jetpack XML-RPC API
5261
	 *
5262
	 * @deprecated 8.0 Use Connection_Manager instead.
5263
	 * @return string
5264
	 */
5265
	public static function xmlrpc_api_url() {
5266
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_api_url()' );
5267
		return self::connection()->xmlrpc_api_url();
5268
	}
5269
5270
	/**
5271
	 * Returns the connection manager object.
5272
	 *
5273
	 * @return Automattic\Jetpack\Connection\Manager
5274
	 */
5275
	public static function connection() {
5276
		$jetpack = static::init();
5277
5278
		// If the connection manager hasn't been instantiated, do that now.
5279
		if ( ! $jetpack->connection_manager ) {
5280
			$jetpack->connection_manager = new Connection_Manager( 'jetpack' );
5281
		}
5282
5283
		return $jetpack->connection_manager;
5284
	}
5285
5286
	/**
5287
	 * Creates two secret tokens and the end of life timestamp for them.
5288
	 *
5289
	 * Note these tokens are unique per call, NOT static per site for connecting.
5290
	 *
5291
	 * @since 2.6
5292
	 * @param String  $action  The action name.
5293
	 * @param Integer $user_id The user identifier.
0 ignored issues
show
Should the type for parameter $user_id not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5294
	 * @param Integer $exp     Expiration time in seconds.
5295
	 * @return array
5296
	 */
5297
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5298
		return self::connection()->generate_secrets( $action, $user_id, $exp );
5299
	}
5300
5301
	public static function get_secrets( $action, $user_id ) {
5302
		$secrets = self::connection()->get_secrets( $action, $user_id );
5303
5304
		if ( Connection_Manager::SECRETS_MISSING === $secrets ) {
5305
			return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'verify_secrets_missing'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5306
		}
5307
5308
		if ( Connection_Manager::SECRETS_EXPIRED === $secrets ) {
5309
			return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'verify_secrets_expired'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5310
		}
5311
5312
		return $secrets;
5313
	}
5314
5315
	/**
5316
	 * @deprecated 7.5 Use Connection_Manager instead.
5317
	 *
5318
	 * @param $action
5319
	 * @param $user_id
5320
	 */
5321
	public static function delete_secrets( $action, $user_id ) {
5322
		return self::connection()->delete_secrets( $action, $user_id );
5323
	}
5324
5325
	/**
5326
	 * Builds the timeout limit for queries talking with the wpcom servers.
5327
	 *
5328
	 * Based on local php max_execution_time in php.ini
5329
	 *
5330
	 * @since 2.6
5331
	 * @return int
5332
	 * @deprecated
5333
	 **/
5334
	public function get_remote_query_timeout_limit() {
5335
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
5336
		return self::get_max_execution_time();
5337
	}
5338
5339
	/**
5340
	 * Builds the timeout limit for queries talking with the wpcom servers.
5341
	 *
5342
	 * Based on local php max_execution_time in php.ini
5343
	 *
5344
	 * @since 5.4
5345
	 * @return int
5346
	 **/
5347
	public static function get_max_execution_time() {
5348
		$timeout = (int) ini_get( 'max_execution_time' );
5349
5350
		// Ensure exec time set in php.ini
5351
		if ( ! $timeout ) {
5352
			$timeout = 30;
5353
		}
5354
		return $timeout;
5355
	}
5356
5357
	/**
5358
	 * Sets a minimum request timeout, and returns the current timeout
5359
	 *
5360
	 * @since 5.4
5361
	 **/
5362 View Code Duplication
	public static function set_min_time_limit( $min_timeout ) {
5363
		$timeout = self::get_max_execution_time();
5364
		if ( $timeout < $min_timeout ) {
5365
			$timeout = $min_timeout;
5366
			set_time_limit( $timeout );
5367
		}
5368
		return $timeout;
5369
	}
5370
5371
	/**
5372
	 * Takes the response from the Jetpack register new site endpoint and
5373
	 * verifies it worked properly.
5374
	 *
5375
	 * @since 2.6
5376
	 * @deprecated since 7.7.0
5377
	 * @see Automattic\Jetpack\Connection\Manager::validate_remote_register_response()
5378
	 **/
5379
	public function validate_remote_register_response() {
5380
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::validate_remote_register_response' );
5381
	}
5382
5383
	/**
5384
	 * @return bool|WP_Error
5385
	 */
5386
	public static function register() {
5387
		$tracking = new Tracking();
5388
		$tracking->record_user_event( 'jpc_register_begin' );
5389
5390
		add_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5391
5392
		$connection   = self::connection();
5393
		$registration = $connection->register();
5394
5395
		remove_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5396
5397
		if ( ! $registration || is_wp_error( $registration ) ) {
5398
			return $registration;
5399
		}
5400
5401
		return true;
5402
	}
5403
5404
	/**
5405
	 * Filters the registration request body to include tracking properties.
5406
	 *
5407
	 * @param array $properties
5408
	 * @return array amended properties.
5409
	 */
5410 View Code Duplication
	public static function filter_register_request_body( $properties ) {
5411
		$tracking        = new Tracking();
5412
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5413
5414
		return array_merge(
5415
			$properties,
5416
			array(
5417
				'_ui' => $tracks_identity['_ui'],
5418
				'_ut' => $tracks_identity['_ut'],
5419
			)
5420
		);
5421
	}
5422
5423
	/**
5424
	 * Filters the token request body to include tracking properties.
5425
	 *
5426
	 * @param array $properties
5427
	 * @return array amended properties.
5428
	 */
5429 View Code Duplication
	public static function filter_token_request_body( $properties ) {
5430
		$tracking        = new Tracking();
5431
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5432
5433
		return array_merge(
5434
			$properties,
5435
			array(
5436
				'_ui' => $tracks_identity['_ui'],
5437
				'_ut' => $tracks_identity['_ut'],
5438
			)
5439
		);
5440
	}
5441
5442
	/**
5443
	 * If the db version is showing something other that what we've got now, bump it to current.
5444
	 *
5445
	 * @return bool: True if the option was incorrect and updated, false if nothing happened.
0 ignored issues
show
The doc-type bool: could not be parsed: Unknown type name "bool:" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
5446
	 */
5447
	public static function maybe_set_version_option() {
5448
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5449
		if ( JETPACK__VERSION != $version ) {
5450
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5451
5452
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5453
				/** This action is documented in class.jetpack.php */
5454
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5455
			}
5456
5457
			return true;
5458
		}
5459
		return false;
5460
	}
5461
5462
	/* Client Server API */
5463
5464
	/**
5465
	 * Loads the Jetpack XML-RPC client.
5466
	 * No longer necessary, as the XML-RPC client will be automagically loaded.
5467
	 *
5468
	 * @deprecated since 7.7.0
5469
	 */
5470
	public static function load_xml_rpc_client() {
5471
		_deprecated_function( __METHOD__, 'jetpack-7.7' );
5472
	}
5473
5474
	/**
5475
	 * Resets the saved authentication state in between testing requests.
5476
	 *
5477
	 * @deprecated since 8.9.0
5478
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::reset_saved_auth_state()
5479
	 */
5480
	public function reset_saved_auth_state() {
5481
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::reset_saved_auth_state' );
5482
		Connection_Rest_Authentication::init()->reset_saved_auth_state();
5483
	}
5484
5485
	/**
5486
	 * Verifies the signature of the current request.
5487
	 *
5488
	 * @deprecated since 7.7.0
5489
	 * @see Automattic\Jetpack\Connection\Manager::verify_xml_rpc_signature()
5490
	 *
5491
	 * @return false|array
5492
	 */
5493
	public function verify_xml_rpc_signature() {
5494
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::verify_xml_rpc_signature' );
5495
		return self::connection()->verify_xml_rpc_signature();
5496
	}
5497
5498
	/**
5499
	 * Verifies the signature of the current request.
5500
	 *
5501
	 * This function has side effects and should not be used. Instead,
5502
	 * use the memoized version `->verify_xml_rpc_signature()`.
5503
	 *
5504
	 * @deprecated since 7.7.0
5505
	 * @see Automattic\Jetpack\Connection\Manager::internal_verify_xml_rpc_signature()
5506
	 * @internal
5507
	 */
5508
	private function internal_verify_xml_rpc_signature() {
5509
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::internal_verify_xml_rpc_signature' );
5510
	}
5511
5512
	/**
5513
	 * Authenticates XML-RPC and other requests from the Jetpack Server.
5514
	 *
5515
	 * @deprecated since 7.7.0
5516
	 * @see Automattic\Jetpack\Connection\Manager::authenticate_jetpack()
5517
	 *
5518
	 * @param \WP_User|mixed $user     User object if authenticated.
5519
	 * @param string         $username Username.
5520
	 * @param string         $password Password string.
5521
	 * @return \WP_User|mixed Authenticated user or error.
5522
	 */
5523 View Code Duplication
	public function authenticate_jetpack( $user, $username, $password ) {
5524
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::authenticate_jetpack' );
5525
5526
		if ( ! $this->connection_manager ) {
5527
			$this->connection_manager = new Connection_Manager();
5528
		}
5529
5530
		return $this->connection_manager->authenticate_jetpack( $user, $username, $password );
5531
	}
5532
5533
	/**
5534
	 * Authenticates requests from Jetpack server to WP REST API endpoints.
5535
	 * Uses the existing XMLRPC request signing implementation.
5536
	 *
5537
	 * @deprecated since 8.9.0
5538
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authenticate()
5539
	 */
5540
	function wp_rest_authenticate( $user ) {
5541
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenticate' );
5542
		return Connection_Rest_Authentication::init()->wp_rest_authenticate( $user );
5543
	}
5544
5545
	/**
5546
	 * Report authentication status to the WP REST API.
5547
	 *
5548
	 * @deprecated since 8.9.0
5549
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authentication_errors()
5550
	 *
5551
	 * @param  WP_Error|mixed $result Error from another authentication handler, null if we should handle it, or another value if not
0 ignored issues
show
There is no parameter named $result. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5552
	 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5553
	 */
5554
	public function wp_rest_authentication_errors( $value ) {
5555
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenication_errors' );
5556
		return Connection_Rest_Authentication::init()->wp_rest_authentication_errors( $value );
5557
	}
5558
5559
	/**
5560
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5561
	 * Capture it here so we can verify the signature later.
5562
	 *
5563
	 * @deprecated since 7.7.0
5564
	 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_methods()
5565
	 *
5566
	 * @param array $methods XMLRPC methods.
5567
	 * @return array XMLRPC methods, with the $HTTP_RAW_POST_DATA one.
5568
	 */
5569 View Code Duplication
	public function xmlrpc_methods( $methods ) {
5570
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_methods' );
5571
5572
		if ( ! $this->connection_manager ) {
5573
			$this->connection_manager = new Connection_Manager();
5574
		}
5575
5576
		return $this->connection_manager->xmlrpc_methods( $methods );
5577
	}
5578
5579
	/**
5580
	 * Register additional public XMLRPC methods.
5581
	 *
5582
	 * @deprecated since 7.7.0
5583
	 * @see Automattic\Jetpack\Connection\Manager::public_xmlrpc_methods()
5584
	 *
5585
	 * @param array $methods Public XMLRPC methods.
5586
	 * @return array Public XMLRPC methods, with the getOptions one.
5587
	 */
5588 View Code Duplication
	public function public_xmlrpc_methods( $methods ) {
5589
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::public_xmlrpc_methods' );
5590
5591
		if ( ! $this->connection_manager ) {
5592
			$this->connection_manager = new Connection_Manager();
5593
		}
5594
5595
		return $this->connection_manager->public_xmlrpc_methods( $methods );
5596
	}
5597
5598
	/**
5599
	 * Handles a getOptions XMLRPC method call.
5600
	 *
5601
	 * @deprecated since 7.7.0
5602
	 * @see Automattic\Jetpack\Connection\Manager::jetpack_getOptions()
5603
	 *
5604
	 * @param array $args method call arguments.
5605
	 * @return array an amended XMLRPC server options array.
5606
	 */
5607 View Code Duplication
	public function jetpack_getOptions( $args ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
5608
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::jetpack_getOptions' );
5609
5610
		if ( ! $this->connection_manager ) {
5611
			$this->connection_manager = new Connection_Manager();
5612
		}
5613
5614
		return $this->connection_manager->jetpack_getOptions( $args );
0 ignored issues
show
The method jetpack_getOptions() does not exist on Automattic\Jetpack\Connection\Manager. Did you maybe mean jetpack_get_options()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
5615
	}
5616
5617
	/**
5618
	 * Adds Jetpack-specific options to the output of the XMLRPC options method.
5619
	 *
5620
	 * @deprecated since 7.7.0
5621
	 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_options()
5622
	 *
5623
	 * @param array $options Standard Core options.
5624
	 * @return array Amended options.
5625
	 */
5626 View Code Duplication
	public function xmlrpc_options( $options ) {
5627
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_options' );
5628
5629
		if ( ! $this->connection_manager ) {
5630
			$this->connection_manager = new Connection_Manager();
5631
		}
5632
5633
		return $this->connection_manager->xmlrpc_options( $options );
5634
	}
5635
5636
	/**
5637
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5638
	 * SET: state( $key, $value );
5639
	 * GET: $value = state( $key );
5640
	 *
5641
	 * @param string $key
0 ignored issues
show
Should the type for parameter $key not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5642
	 * @param string $value
0 ignored issues
show
Should the type for parameter $value not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5643
	 * @param bool   $restate private
5644
	 */
5645
	public static function state( $key = null, $value = null, $restate = false ) {
5646
		static $state = array();
5647
		static $path, $domain;
5648
		if ( ! isset( $path ) ) {
5649
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
5650
			$admin_url = self::admin_url();
5651
			$bits      = wp_parse_url( $admin_url );
5652
5653
			if ( is_array( $bits ) ) {
5654
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5655
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5656
			} else {
5657
				$path = $domain = null;
5658
			}
5659
		}
5660
5661
		// Extract state from cookies and delete cookies
5662
		if ( isset( $_COOKIE['jetpackState'] ) && is_array( $_COOKIE['jetpackState'] ) ) {
5663
			$yum = wp_unslash( $_COOKIE['jetpackState'] );
5664
			unset( $_COOKIE['jetpackState'] );
5665
			foreach ( $yum as $k => $v ) {
0 ignored issues
show
The expression $yum of type string|array<integer,string> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
5666
				if ( strlen( $v ) ) {
5667
					$state[ $k ] = $v;
5668
				}
5669
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5670
			}
5671
		}
5672
5673
		if ( $restate ) {
5674
			foreach ( $state as $k => $v ) {
5675
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5676
			}
5677
			return;
5678
		}
5679
5680
		// Get a state variable.
5681
		if ( isset( $key ) && ! isset( $value ) ) {
5682
			if ( array_key_exists( $key, $state ) ) {
5683
				return $state[ $key ];
5684
			}
5685
			return null;
5686
		}
5687
5688
		// Set a state variable.
5689
		if ( isset( $key ) && isset( $value ) ) {
5690
			if ( is_array( $value ) && isset( $value[0] ) ) {
5691
				$value = $value[0];
5692
			}
5693
			$state[ $key ] = $value;
5694
			if ( ! headers_sent() ) {
5695
				if ( self::should_set_cookie( $key ) ) {
5696
					setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5697
				}
5698
			}
5699
		}
5700
	}
5701
5702
	public static function restate() {
5703
		self::state( null, null, true );
5704
	}
5705
5706
	/**
5707
	 * Determines whether the jetpackState[$key] value should be added to the
5708
	 * cookie.
5709
	 *
5710
	 * @param string $key The state key.
5711
	 *
5712
	 * @return boolean Whether the value should be added to the cookie.
5713
	 */
5714
	public static function should_set_cookie( $key ) {
5715
		global $current_screen;
5716
		$page = isset( $current_screen->base ) ? $current_screen->base : null;
5717
5718
		if ( 'toplevel_page_jetpack' === $page && 'display_update_modal' === $key ) {
5719
			return false;
5720
		}
5721
5722
		return true;
5723
	}
5724
5725
	public static function check_privacy( $file ) {
5726
		static $is_site_publicly_accessible = null;
5727
5728
		if ( is_null( $is_site_publicly_accessible ) ) {
5729
			$is_site_publicly_accessible = false;
5730
5731
			$rpc = new Jetpack_IXR_Client();
5732
5733
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5734
			if ( $success ) {
5735
				$response = $rpc->getResponse();
5736
				if ( $response ) {
5737
					$is_site_publicly_accessible = true;
5738
				}
5739
			}
5740
5741
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5742
		}
5743
5744
		if ( $is_site_publicly_accessible ) {
5745
			return;
5746
		}
5747
5748
		$module_slug = self::get_module_slug( $file );
5749
5750
		$privacy_checks = self::state( 'privacy_checks' );
5751
		if ( ! $privacy_checks ) {
5752
			$privacy_checks = $module_slug;
5753
		} else {
5754
			$privacy_checks .= ",$module_slug";
5755
		}
5756
5757
		self::state( 'privacy_checks', $privacy_checks );
5758
	}
5759
5760
	/**
5761
	 * Helper method for multicall XMLRPC.
5762
	 *
5763
	 * @deprecated since 8.9.0
5764
	 * @see Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call()
5765
	 *
5766
	 * @param ...$args Args for the async_call.
5767
	 */
5768
	public static function xmlrpc_async_call( ...$args ) {
5769
5770
		_deprecated_function( 'Jetpack::xmlrpc_async_call', 'jetpack-8.9.0', 'Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call' );
5771
5772
		global $blog_id;
5773
		static $clients = array();
5774
5775
		$client_blog_id = is_multisite() ? $blog_id : 0;
5776
5777
		if ( ! isset( $clients[ $client_blog_id ] ) ) {
5778
			$clients[ $client_blog_id ] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => Connection_Manager::CONNECTION_OWNER ) );
5779
			if ( function_exists( 'ignore_user_abort' ) ) {
5780
				ignore_user_abort( true );
5781
			}
5782
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5783
		}
5784
5785
		if ( ! empty( $args[0] ) ) {
5786
			call_user_func_array( array( $clients[ $client_blog_id ], 'addCall' ), $args );
5787
		} elseif ( is_multisite() ) {
5788
			foreach ( $clients as $client_blog_id => $client ) {
5789
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5790
					continue;
5791
				}
5792
5793
				$switch_success = switch_to_blog( $client_blog_id, true );
5794
				if ( ! $switch_success ) {
5795
					continue;
5796
				}
5797
5798
				flush();
5799
				$client->query();
5800
5801
				restore_current_blog();
5802
			}
5803
		} else {
5804
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5805
				flush();
5806
				$clients[0]->query();
5807
			}
5808
		}
5809
	}
5810
5811
	/**
5812
	 * Serve a WordPress.com static resource via a randomized wp.com subdomain.
5813
	 *
5814
	 * @deprecated 9.3.0 Use Assets::staticize_subdomain.
5815
	 *
5816
	 * @param string $url WordPress.com static resource URL.
5817
	 */
5818
	public static function staticize_subdomain( $url ) {
5819
		_deprecated_function( __METHOD__, 'jetpack-9.3.0', 'Automattic\Jetpack\Assets::staticize_subdomain' );
5820
		return Assets::staticize_subdomain( $url );
5821
	}
5822
5823
	/* JSON API Authorization */
5824
5825
	/**
5826
	 * Handles the login action for Authorizing the JSON API
5827
	 */
5828
	function login_form_json_api_authorization() {
5829
		$this->verify_json_api_authorization_request();
5830
5831
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
5832
5833
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
5834
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5835
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
5836
	}
5837
5838
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
5839
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
5840
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
5841
			return $url;
5842
		}
5843
5844
		$parsed_url = wp_parse_url( $url );
5845
		$url        = strtok( $url, '?' );
5846
		$url        = "$url?{$_SERVER['QUERY_STRING']}";
5847
		if ( ! empty( $parsed_url['query'] ) ) {
5848
			$url .= "&{$parsed_url['query']}";
5849
		}
5850
5851
		return $url;
5852
	}
5853
5854
	// Make sure the POSTed request is handled by the same action
5855
	function preserve_action_in_login_form_for_json_api_authorization() {
5856
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
5857
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
5858
	}
5859
5860
	// If someone logs in to approve API access, store the Access Code in usermeta
5861
	function store_json_api_authorization_token( $user_login, $user ) {
5862
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5863
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
5864
		$token = wp_generate_password( 32, false );
5865
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5866
	}
5867
5868
	// Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5869
	function allow_wpcom_public_api_domain( $domains ) {
5870
		$domains[] = 'public-api.wordpress.com';
5871
		return $domains;
5872
	}
5873
5874
	static function is_redirect_encoded( $redirect_url ) {
5875
		return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5876
	}
5877
5878
	// Add all wordpress.com environments to the safe redirect allowed list.
5879
	function allow_wpcom_environments( $domains ) {
5880
		$domains[] = 'wordpress.com';
5881
		$domains[] = 'wpcalypso.wordpress.com';
5882
		$domains[] = 'horizon.wordpress.com';
5883
		$domains[] = 'calypso.localhost';
5884
		return $domains;
5885
	}
5886
5887
	// Add the Access Code details to the public-api.wordpress.com redirect
5888
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5889
		return add_query_arg(
5890
			urlencode_deep(
5891
				array(
5892
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5893
					'jetpack-user-id' => (int) $user->ID,
5894
					'jetpack-state'   => $this->json_api_authorization_request['state'],
5895
				)
5896
			),
5897
			$redirect_to
5898
		);
5899
	}
5900
5901
	/**
5902
	 * Verifies the request by checking the signature
5903
	 *
5904
	 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5905
	 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5906
	 *
5907
	 * @param null|array $environment
5908
	 */
5909
	function verify_json_api_authorization_request( $environment = null ) {
5910
		$environment = is_null( $environment )
5911
			? $_REQUEST
5912
			: $environment;
5913
5914
		list( $envToken, $envVersion, $envUserId ) = explode( ':', $environment['token'] );
0 ignored issues
show
The assignment to $envVersion is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
5915
		$token                                     = Jetpack_Data::get_access_token( $envUserId, $envToken );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
5916
		if ( ! $token || empty( $token->secret ) ) {
5917
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5918
		}
5919
5920
		$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' );
5921
5922
		// Host has encoded the request URL, probably as a result of a bad http => https redirect
5923
		if ( self::is_redirect_encoded( $_GET['redirect_to'] ) ) {
5924
			/**
5925
			 * Jetpack authorisation request Error.
5926
			 *
5927
			 * @since 7.5.0
5928
			 */
5929
			do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5930
			$die_error = sprintf(
5931
				/* translators: %s is a URL */
5932
				__( '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' ),
5933
				Redirect::get_url( 'jetpack-support-double-encoding' )
5934
			);
5935
		}
5936
5937
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5938
5939
		if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5940
			$signature = $jetpack_signature->sign_request(
5941
				$environment['token'],
5942
				$environment['timestamp'],
5943
				$environment['nonce'],
5944
				'',
5945
				'GET',
5946
				$environment['jetpack_json_api_original_query'],
5947
				null,
5948
				true
5949
			);
5950
		} else {
5951
			$signature = $jetpack_signature->sign_current_request(
5952
				array(
5953
					'body'   => null,
5954
					'method' => 'GET',
5955
				)
5956
			);
5957
		}
5958
5959
		if ( ! $signature ) {
5960
			wp_die( $die_error );
5961
		} elseif ( is_wp_error( $signature ) ) {
5962
			wp_die( $die_error );
5963
		} elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
5964
			if ( is_ssl() ) {
5965
				// 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
5966
				$signature = $jetpack_signature->sign_current_request(
5967
					array(
5968
						'scheme' => 'http',
5969
						'body'   => null,
5970
						'method' => 'GET',
5971
					)
5972
				);
5973
				if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5974
					wp_die( $die_error );
5975
				}
5976
			} else {
5977
				wp_die( $die_error );
5978
			}
5979
		}
5980
5981
		$timestamp = (int) $environment['timestamp'];
5982
		$nonce     = stripslashes( (string) $environment['nonce'] );
5983
5984
		if ( ! $this->connection_manager ) {
5985
			$this->connection_manager = new Connection_Manager();
5986
		}
5987
5988
		if ( ! $this->connection_manager->add_nonce( $timestamp, $nonce ) ) {
5989
			// De-nonce the nonce, at least for 5 minutes.
5990
			// 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)
5991
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5992
			if ( $old_nonce_time < time() - 300 ) {
5993
				wp_die( __( 'The authorization process expired.  Please go back and try again.', 'jetpack' ) );
5994
			}
5995
		}
5996
5997
		$data         = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
5998
		$data_filters = array(
5999
			'state'        => 'opaque',
6000
			'client_id'    => 'int',
6001
			'client_title' => 'string',
6002
			'client_image' => 'url',
6003
		);
6004
6005
		foreach ( $data_filters as $key => $sanitation ) {
6006
			if ( ! isset( $data->$key ) ) {
6007
				wp_die( $die_error );
6008
			}
6009
6010
			switch ( $sanitation ) {
6011
				case 'int':
6012
					$this->json_api_authorization_request[ $key ] = (int) $data->$key;
6013
					break;
6014
				case 'opaque':
6015
					$this->json_api_authorization_request[ $key ] = (string) $data->$key;
6016
					break;
6017
				case 'string':
6018
					$this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
6019
					break;
6020
				case 'url':
6021
					$this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
6022
					break;
6023
			}
6024
		}
6025
6026
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
6027
			wp_die( $die_error );
6028
		}
6029
	}
6030
6031
	function login_message_json_api_authorization( $message ) {
6032
		return '<p class="message">' . sprintf(
6033
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.', 'jetpack' ),
6034
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
6035
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
6036
	}
6037
6038
	/**
6039
	 * Get $content_width, but with a <s>twist</s> filter.
6040
	 */
6041
	public static function get_content_width() {
6042
		$content_width = ( isset( $GLOBALS['content_width'] ) && is_numeric( $GLOBALS['content_width'] ) )
6043
			? $GLOBALS['content_width']
6044
			: false;
6045
		/**
6046
		 * Filter the Content Width value.
6047
		 *
6048
		 * @since 2.2.3
6049
		 *
6050
		 * @param string $content_width Content Width value.
6051
		 */
6052
		return apply_filters( 'jetpack_content_width', $content_width );
6053
	}
6054
6055
	/**
6056
	 * Pings the WordPress.com Mirror Site for the specified options.
6057
	 *
6058
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6059
	 *
6060
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6061
	 */
6062
	public function get_cloud_site_options( $option_names ) {
6063
		$option_names = array_filter( (array) $option_names, 'is_string' );
6064
6065
		$xml = new Jetpack_IXR_Client();
6066
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
6067
		if ( $xml->isError() ) {
6068
			return array(
6069
				'error_code' => $xml->getErrorCode(),
6070
				'error_msg'  => $xml->getErrorMessage(),
6071
			);
6072
		}
6073
		$cloud_site_options = $xml->getResponse();
6074
6075
		return $cloud_site_options;
6076
	}
6077
6078
	/**
6079
	 * Checks if the site is currently in an identity crisis.
6080
	 *
6081
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6082
	 */
6083
	public static function check_identity_crisis() {
6084
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() || ! self::validate_sync_error_idc_option() ) {
6085
			return false;
6086
		}
6087
6088
		return Jetpack_Options::get_option( 'sync_error_idc' );
6089
	}
6090
6091
	/**
6092
	 * Checks whether the home and siteurl specifically are allowed.
6093
	 * Written so that we don't have re-check $key and $value params every time
6094
	 * we want to check if this site is allowed, for example in footer.php
6095
	 *
6096
	 * @since  3.8.0
6097
	 * @return bool True = already allowed False = not on the allowed list.
6098
	 */
6099
	public static function is_staging_site() {
6100
		_deprecated_function( 'Jetpack::is_staging_site', 'jetpack-8.1', '/Automattic/Jetpack/Status->is_staging_site' );
6101
		return ( new Status() )->is_staging_site();
6102
	}
6103
6104
	/**
6105
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6106
	 *
6107
	 * @since 4.4.0
6108
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6109
	 *
6110
	 * @return bool
6111
	 */
6112
	public static function validate_sync_error_idc_option() {
6113
		$is_valid = false;
6114
6115
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
6116
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
6117
		if ( $sync_error && self::sync_idc_optin() ) {
6118
			$local_options = self::get_sync_error_idc_option();
6119
			// Ensure all values are set.
6120
			if ( isset( $sync_error['home'] ) && isset( $local_options['home'] ) && isset( $sync_error['siteurl'] ) && isset( $local_options['siteurl'] ) ) {
6121
6122
				// If the WP.com expected home and siteurl match local home and siteurl it is not valid IDC.
6123
				if (
6124
						isset( $sync_error['wpcom_home'] ) &&
6125
						isset( $sync_error['wpcom_siteurl'] ) &&
6126
						$sync_error['wpcom_home'] === $local_options['home'] &&
6127
						$sync_error['wpcom_siteurl'] === $local_options['siteurl']
6128
				) {
6129
					$is_valid = false;
6130
					// Enable migrate_for_idc so that sync actions are accepted.
6131
					Jetpack_Options::update_option( 'migrate_for_idc', true );
6132
				} elseif ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
6133
					$is_valid = true;
6134
				}
6135
			}
6136
		}
6137
6138
		/**
6139
		 * Filters whether the sync_error_idc option is valid.
6140
		 *
6141
		 * @since 4.4.0
6142
		 *
6143
		 * @param bool $is_valid If the sync_error_idc is valid or not.
6144
		 */
6145
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
6146
6147
		if ( ! $is_valid && $sync_error ) {
6148
			// Since the option exists, and did not validate, delete it
6149
			Jetpack_Options::delete_option( 'sync_error_idc' );
6150
		}
6151
6152
		return $is_valid;
6153
	}
6154
6155
	/**
6156
	 * Normalizes a url by doing three things:
6157
	 *  - Strips protocol
6158
	 *  - Strips www
6159
	 *  - Adds a trailing slash
6160
	 *
6161
	 * @since 4.4.0
6162
	 * @param string $url
6163
	 * @return WP_Error|string
6164
	 */
6165
	public static function normalize_url_protocol_agnostic( $url ) {
6166
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6167 View Code Duplication
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed_url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
6168
			return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'cannot_parse_url'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
6169
		}
6170
6171
		// Strip www and protocols
6172
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6173
		return $url;
6174
	}
6175
6176
	/**
6177
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6178
	 *
6179
	 * @since 4.4.0
6180
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6181
	 *
6182
	 * @param array $response
6183
	 * @return array Array of the local urls, wpcom urls, and error code
6184
	 */
6185
	public static function get_sync_error_idc_option( $response = array() ) {
6186
		// Since the local options will hit the database directly, store the values
6187
		// in a transient to allow for autoloading and caching on subsequent views.
6188
		$local_options = get_transient( 'jetpack_idc_local' );
6189
		if ( false === $local_options ) {
6190
			$local_options = array(
6191
				'home'    => Functions::home_url(),
6192
				'siteurl' => Functions::site_url(),
6193
			);
6194
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
6195
		}
6196
6197
		$options = array_merge( $local_options, $response );
6198
6199
		$returned_values = array();
6200
		foreach ( $options as $key => $option ) {
6201
			if ( 'error_code' === $key ) {
6202
				$returned_values[ $key ] = $option;
6203
				continue;
6204
			}
6205
6206
			if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) {
6207
				continue;
6208
			}
6209
6210
			$returned_values[ $key ] = $normalized_url;
6211
		}
6212
6213
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
6214
6215
		return $returned_values;
6216
	}
6217
6218
	/**
6219
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6220
	 * If set to true, the site will be put into staging mode.
6221
	 *
6222
	 * @since 4.3.2
6223
	 * @return bool
6224
	 */
6225
	public static function sync_idc_optin() {
6226
		if ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
6227
			$default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
6228
		} else {
6229
			$default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
6230
		}
6231
6232
		/**
6233
		 * Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
6234
		 * URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
6235
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
6236
		 *
6237
		 * @since 4.3.2
6238
		 *
6239
		 * @param bool $default Whether the site is opted in to IDC mitigation.
6240
		 */
6241
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
6242
	}
6243
6244
	/**
6245
	 * Maybe Use a .min.css stylesheet, maybe not.
6246
	 *
6247
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6248
	 */
6249
	public static function maybe_min_asset( $url, $path, $plugin ) {
6250
		// Short out on things trying to find actual paths.
6251
		if ( ! $path || empty( $plugin ) ) {
6252
			return $url;
6253
		}
6254
6255
		$path = ltrim( $path, '/' );
6256
6257
		// Strip out the abspath.
6258
		$base = dirname( plugin_basename( $plugin ) );
6259
6260
		// Short out on non-Jetpack assets.
6261
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6262
			return $url;
6263
		}
6264
6265
		// File name parsing.
6266
		$file              = "{$base}/{$path}";
6267
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6268
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6269
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6270
		$extension         = array_shift( $file_name_parts_r );
6271
6272
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6273
			// Already pointing at the minified version.
6274
			if ( 'min' === $file_name_parts_r[0] ) {
6275
				return $url;
6276
			}
6277
6278
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6279
			if ( file_exists( $min_full_path ) ) {
6280
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6281
				// If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6282
				if ( 'css' === $extension ) {
6283
					$key                      = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6284
					self::$min_assets[ $key ] = $path;
6285
				}
6286
			}
6287
		}
6288
6289
		return $url;
6290
	}
6291
6292
	/**
6293
	 * If the asset is minified, let's flag .min as the suffix.
6294
	 *
6295
	 * Attached to `style_loader_src` filter.
6296
	 *
6297
	 * @param string $tag The tag that would link to the external asset.
0 ignored issues
show
There is no parameter named $tag. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6298
	 * @param string $handle The registered handle of the script in question.
6299
	 * @param string $href The url of the asset in question.
0 ignored issues
show
There is no parameter named $href. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6300
	 */
6301
	public static function set_suffix_on_min( $src, $handle ) {
6302
		if ( false === strpos( $src, '.min.css' ) ) {
6303
			return $src;
6304
		}
6305
6306
		if ( ! empty( self::$min_assets ) ) {
6307
			foreach ( self::$min_assets as $file => $path ) {
6308
				if ( false !== strpos( $src, $file ) ) {
6309
					wp_style_add_data( $handle, 'suffix', '.min' );
6310
					return $src;
6311
				}
6312
			}
6313
		}
6314
6315
		return $src;
6316
	}
6317
6318
	/**
6319
	 * Maybe inlines a stylesheet.
6320
	 *
6321
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6322
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6323
	 *
6324
	 * Attached to `style_loader_tag` filter.
6325
	 *
6326
	 * @param string $tag The tag that would link to the external asset.
6327
	 * @param string $handle The registered handle of the script in question.
6328
	 *
6329
	 * @return string
6330
	 */
6331
	public static function maybe_inline_style( $tag, $handle ) {
6332
		global $wp_styles;
6333
		$item = $wp_styles->registered[ $handle ];
6334
6335
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6336
			return $tag;
6337
		}
6338
6339
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6340
			$href = $matches[1];
6341
			// Strip off query string
6342
			if ( $pos = strpos( $href, '?' ) ) {
6343
				$href = substr( $href, 0, $pos );
6344
			}
6345
			// Strip off fragment
6346
			if ( $pos = strpos( $href, '#' ) ) {
6347
				$href = substr( $href, 0, $pos );
6348
			}
6349
		} else {
6350
			return $tag;
6351
		}
6352
6353
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6354
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6355
			return $tag;
6356
		}
6357
6358
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6359
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6360
			// And this isn't the pass that actually deals with the RTL version...
6361
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6362
				// Short out, as the RTL version will deal with it in a moment.
6363
				return $tag;
6364
			}
6365
		}
6366
6367
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6368
		$css  = self::absolutize_css_urls( file_get_contents( $file ), $href );
6369
		if ( $css ) {
6370
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6371
			if ( empty( $item->extra['after'] ) ) {
6372
				wp_add_inline_style( $handle, $css );
6373
			} else {
6374
				array_unshift( $item->extra['after'], $css );
6375
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6376
			}
6377
		}
6378
6379
		return $tag;
6380
	}
6381
6382
	/**
6383
	 * Loads a view file from the views
6384
	 *
6385
	 * Data passed in with the $data parameter will be available in the
6386
	 * template file as $data['value']
6387
	 *
6388
	 * @param string $template - Template file to load
6389
	 * @param array  $data - Any data to pass along to the template
6390
	 * @return boolean - If template file was found
6391
	 **/
6392
	public function load_view( $template, $data = array() ) {
6393
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6394
6395
		if ( file_exists( $views_dir . $template ) ) {
6396
			require_once $views_dir . $template;
6397
			return true;
6398
		}
6399
6400
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6401
		return false;
6402
	}
6403
6404
	/**
6405
	 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
6406
	 */
6407
	public function deprecated_hooks() {
6408
		$filter_deprecated_list = array(
6409
			'jetpack_bail_on_shortcode'                    => array(
6410
				'replacement' => 'jetpack_shortcodes_to_include',
6411
				'version'     => 'jetpack-3.1.0',
6412
			),
6413
			'wpl_sharing_2014_1'                           => array(
6414
				'replacement' => null,
6415
				'version'     => 'jetpack-3.6.0',
6416
			),
6417
			'jetpack-tools-to-include'                     => array(
6418
				'replacement' => 'jetpack_tools_to_include',
6419
				'version'     => 'jetpack-3.9.0',
6420
			),
6421
			'jetpack_identity_crisis_options_to_check'     => array(
6422
				'replacement' => null,
6423
				'version'     => 'jetpack-4.0.0',
6424
			),
6425
			'update_option_jetpack_single_user_site'       => array(
6426
				'replacement' => null,
6427
				'version'     => 'jetpack-4.3.0',
6428
			),
6429
			'audio_player_default_colors'                  => array(
6430
				'replacement' => null,
6431
				'version'     => 'jetpack-4.3.0',
6432
			),
6433
			'add_option_jetpack_featured_images_enabled'   => array(
6434
				'replacement' => null,
6435
				'version'     => 'jetpack-4.3.0',
6436
			),
6437
			'add_option_jetpack_update_details'            => array(
6438
				'replacement' => null,
6439
				'version'     => 'jetpack-4.3.0',
6440
			),
6441
			'add_option_jetpack_updates'                   => array(
6442
				'replacement' => null,
6443
				'version'     => 'jetpack-4.3.0',
6444
			),
6445
			'add_option_jetpack_network_name'              => array(
6446
				'replacement' => null,
6447
				'version'     => 'jetpack-4.3.0',
6448
			),
6449
			'add_option_jetpack_network_allow_new_registrations' => array(
6450
				'replacement' => null,
6451
				'version'     => 'jetpack-4.3.0',
6452
			),
6453
			'add_option_jetpack_network_add_new_users'     => array(
6454
				'replacement' => null,
6455
				'version'     => 'jetpack-4.3.0',
6456
			),
6457
			'add_option_jetpack_network_site_upload_space' => array(
6458
				'replacement' => null,
6459
				'version'     => 'jetpack-4.3.0',
6460
			),
6461
			'add_option_jetpack_network_upload_file_types' => array(
6462
				'replacement' => null,
6463
				'version'     => 'jetpack-4.3.0',
6464
			),
6465
			'add_option_jetpack_network_enable_administration_menus' => array(
6466
				'replacement' => null,
6467
				'version'     => 'jetpack-4.3.0',
6468
			),
6469
			'add_option_jetpack_is_multi_site'             => array(
6470
				'replacement' => null,
6471
				'version'     => 'jetpack-4.3.0',
6472
			),
6473
			'add_option_jetpack_is_main_network'           => array(
6474
				'replacement' => null,
6475
				'version'     => 'jetpack-4.3.0',
6476
			),
6477
			'add_option_jetpack_main_network_site'         => array(
6478
				'replacement' => null,
6479
				'version'     => 'jetpack-4.3.0',
6480
			),
6481
			'jetpack_sync_all_registered_options'          => array(
6482
				'replacement' => null,
6483
				'version'     => 'jetpack-4.3.0',
6484
			),
6485
			'jetpack_has_identity_crisis'                  => array(
6486
				'replacement' => 'jetpack_sync_error_idc_validation',
6487
				'version'     => 'jetpack-4.4.0',
6488
			),
6489
			'jetpack_is_post_mailable'                     => array(
6490
				'replacement' => null,
6491
				'version'     => 'jetpack-4.4.0',
6492
			),
6493
			'jetpack_seo_site_host'                        => array(
6494
				'replacement' => null,
6495
				'version'     => 'jetpack-5.1.0',
6496
			),
6497
			'jetpack_installed_plugin'                     => array(
6498
				'replacement' => 'jetpack_plugin_installed',
6499
				'version'     => 'jetpack-6.0.0',
6500
			),
6501
			'jetpack_holiday_snow_option_name'             => array(
6502
				'replacement' => null,
6503
				'version'     => 'jetpack-6.0.0',
6504
			),
6505
			'jetpack_holiday_chance_of_snow'               => array(
6506
				'replacement' => null,
6507
				'version'     => 'jetpack-6.0.0',
6508
			),
6509
			'jetpack_holiday_snow_js_url'                  => array(
6510
				'replacement' => null,
6511
				'version'     => 'jetpack-6.0.0',
6512
			),
6513
			'jetpack_is_holiday_snow_season'               => array(
6514
				'replacement' => null,
6515
				'version'     => 'jetpack-6.0.0',
6516
			),
6517
			'jetpack_holiday_snow_option_updated'          => array(
6518
				'replacement' => null,
6519
				'version'     => 'jetpack-6.0.0',
6520
			),
6521
			'jetpack_holiday_snowing'                      => array(
6522
				'replacement' => null,
6523
				'version'     => 'jetpack-6.0.0',
6524
			),
6525
			'jetpack_sso_auth_cookie_expirtation'          => array(
6526
				'replacement' => 'jetpack_sso_auth_cookie_expiration',
6527
				'version'     => 'jetpack-6.1.0',
6528
			),
6529
			'jetpack_cache_plans'                          => array(
6530
				'replacement' => null,
6531
				'version'     => 'jetpack-6.1.0',
6532
			),
6533
6534
			'jetpack_lazy_images_skip_image_with_atttributes' => array(
6535
				'replacement' => 'jetpack_lazy_images_skip_image_with_attributes',
6536
				'version'     => 'jetpack-6.5.0',
6537
			),
6538
			'jetpack_enable_site_verification'             => array(
6539
				'replacement' => null,
6540
				'version'     => 'jetpack-6.5.0',
6541
			),
6542
			'can_display_jetpack_manage_notice'            => array(
6543
				'replacement' => null,
6544
				'version'     => 'jetpack-7.3.0',
6545
			),
6546
			'atd_http_post_timeout'                        => array(
6547
				'replacement' => null,
6548
				'version'     => 'jetpack-7.3.0',
6549
			),
6550
			'atd_service_domain'                           => array(
6551
				'replacement' => null,
6552
				'version'     => 'jetpack-7.3.0',
6553
			),
6554
			'atd_load_scripts'                             => array(
6555
				'replacement' => null,
6556
				'version'     => 'jetpack-7.3.0',
6557
			),
6558
			'jetpack_widget_authors_exclude'               => array(
6559
				'replacement' => 'jetpack_widget_authors_params',
6560
				'version'     => 'jetpack-7.7.0',
6561
			),
6562
			// Removed in Jetpack 7.9.0
6563
			'jetpack_pwa_manifest'                         => array(
6564
				'replacement' => null,
6565
				'version'     => 'jetpack-7.9.0',
6566
			),
6567
			'jetpack_pwa_background_color'                 => array(
6568
				'replacement' => null,
6569
				'version'     => 'jetpack-7.9.0',
6570
			),
6571
			'jetpack_check_mobile'                         => array(
6572
				'replacement' => null,
6573
				'version'     => 'jetpack-8.3.0',
6574
			),
6575
			'jetpack_mobile_stylesheet'                    => array(
6576
				'replacement' => null,
6577
				'version'     => 'jetpack-8.3.0',
6578
			),
6579
			'jetpack_mobile_template'                      => array(
6580
				'replacement' => null,
6581
				'version'     => 'jetpack-8.3.0',
6582
			),
6583
			'jetpack_mobile_theme_menu'                    => array(
6584
				'replacement' => null,
6585
				'version'     => 'jetpack-8.3.0',
6586
			),
6587
			'minileven_show_featured_images'               => array(
6588
				'replacement' => null,
6589
				'version'     => 'jetpack-8.3.0',
6590
			),
6591
			'minileven_attachment_size'                    => array(
6592
				'replacement' => null,
6593
				'version'     => 'jetpack-8.3.0',
6594
			),
6595
			'instagram_cache_oembed_api_response_body'     => array(
6596
				'replacement' => null,
6597
				'version'     => 'jetpack-9.1.0',
6598
			),
6599
			'jetpack_can_make_outbound_https'              => array(
6600
				'replacement' => null,
6601
				'version'     => 'jetpack-9.1.0',
6602
			),
6603
		);
6604
6605
		foreach ( $filter_deprecated_list as $tag => $args ) {
6606
			if ( has_filter( $tag ) ) {
6607
				apply_filters_deprecated( $tag, array( null ), $args['version'], $args['replacement'] );
6608
			}
6609
		}
6610
6611
		$action_deprecated_list = array(
6612
			'jetpack_updated_theme'        => array(
6613
				'replacement' => 'jetpack_updated_themes',
6614
				'version'     => 'jetpack-6.2.0',
6615
			),
6616
			'atd_http_post_error'          => array(
6617
				'replacement' => null,
6618
				'version'     => 'jetpack-7.3.0',
6619
			),
6620
			'mobile_reject_mobile'         => array(
6621
				'replacement' => null,
6622
				'version'     => 'jetpack-8.3.0',
6623
			),
6624
			'mobile_force_mobile'          => array(
6625
				'replacement' => null,
6626
				'version'     => 'jetpack-8.3.0',
6627
			),
6628
			'mobile_app_promo_download'    => array(
6629
				'replacement' => null,
6630
				'version'     => 'jetpack-8.3.0',
6631
			),
6632
			'mobile_setup'                 => array(
6633
				'replacement' => null,
6634
				'version'     => 'jetpack-8.3.0',
6635
			),
6636
			'jetpack_mobile_footer_before' => array(
6637
				'replacement' => null,
6638
				'version'     => 'jetpack-8.3.0',
6639
			),
6640
			'wp_mobile_theme_footer'       => array(
6641
				'replacement' => null,
6642
				'version'     => 'jetpack-8.3.0',
6643
			),
6644
			'minileven_credits'            => array(
6645
				'replacement' => null,
6646
				'version'     => 'jetpack-8.3.0',
6647
			),
6648
			'jetpack_mobile_header_before' => array(
6649
				'replacement' => null,
6650
				'version'     => 'jetpack-8.3.0',
6651
			),
6652
			'jetpack_mobile_header_after'  => array(
6653
				'replacement' => null,
6654
				'version'     => 'jetpack-8.3.0',
6655
			),
6656
		);
6657
6658
		foreach ( $action_deprecated_list as $tag => $args ) {
6659
			if ( has_action( $tag ) ) {
6660
				do_action_deprecated( $tag, array(), $args['version'], $args['replacement'] );
6661
			}
6662
		}
6663
	}
6664
6665
	/**
6666
	 * Converts any url in a stylesheet, to the correct absolute url.
6667
	 *
6668
	 * Considerations:
6669
	 *  - Normal, relative URLs     `feh.png`
6670
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6671
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6672
	 *  - Absolute URLs             `http://domain.com/feh.png`
6673
	 *  - Domain root relative URLs `/feh.png`
6674
	 *
6675
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6676
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6677
	 *
6678
	 * @return mixed|string
6679
	 */
6680
	public static function absolutize_css_urls( $css, $css_file_url ) {
6681
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6682
		$css_dir = dirname( $css_file_url );
6683
		$p       = wp_parse_url( $css_dir );
6684
		$domain  = sprintf(
6685
			'%1$s//%2$s%3$s%4$s',
6686
			isset( $p['scheme'] ) ? "{$p['scheme']}:" : '',
6687
			isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6688
			$p['host'],
6689
			isset( $p['port'] ) ? ":{$p['port']}" : ''
6690
		);
6691
6692
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6693
			$find = $replace = array();
6694
			foreach ( $matches as $match ) {
6695
				$url = trim( $match['path'], "'\" \t" );
6696
6697
				// If this is a data url, we don't want to mess with it.
6698
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6699
					continue;
6700
				}
6701
6702
				// If this is an absolute or protocol-agnostic url,
6703
				// we don't want to mess with it.
6704
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6705
					continue;
6706
				}
6707
6708
				switch ( substr( $url, 0, 1 ) ) {
6709
					case '/':
6710
						$absolute = $domain . $url;
6711
						break;
6712
					default:
6713
						$absolute = $css_dir . '/' . $url;
6714
				}
6715
6716
				$find[]    = $match[0];
6717
				$replace[] = sprintf( 'url("%s")', $absolute );
6718
			}
6719
			$css = str_replace( $find, $replace, $css );
6720
		}
6721
6722
		return $css;
6723
	}
6724
6725
	/**
6726
	 * This methods removes all of the registered css files on the front end
6727
	 * from Jetpack in favor of using a single file. In effect "imploding"
6728
	 * all the files into one file.
6729
	 *
6730
	 * Pros:
6731
	 * - Uses only ONE css asset connection instead of 15
6732
	 * - Saves a minimum of 56k
6733
	 * - Reduces server load
6734
	 * - Reduces time to first painted byte
6735
	 *
6736
	 * Cons:
6737
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6738
	 *      should not cause any issues with themes.
6739
	 * - Plugins/themes dequeuing styles no longer do anything. See
6740
	 *      jetpack_implode_frontend_css filter for a workaround
6741
	 *
6742
	 * For some situations developers may wish to disable css imploding and
6743
	 * instead operate in legacy mode where each file loads seperately and
6744
	 * can be edited individually or dequeued. This can be accomplished with
6745
	 * the following line:
6746
	 *
6747
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6748
	 *
6749
	 * @since 3.2
6750
	 **/
6751
	public function implode_frontend_css( $travis_test = false ) {
6752
		$do_implode = true;
6753
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6754
			$do_implode = false;
6755
		}
6756
6757
		// Do not implode CSS when the page loads via the AMP plugin.
6758
		if ( Jetpack_AMP_Support::is_amp_request() ) {
6759
			$do_implode = false;
6760
		}
6761
6762
		/**
6763
		 * Allow CSS to be concatenated into a single jetpack.css file.
6764
		 *
6765
		 * @since 3.2.0
6766
		 *
6767
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6768
		 */
6769
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6770
6771
		// Do not use the imploded file when default behavior was altered through the filter
6772
		if ( ! $do_implode ) {
6773
			return;
6774
		}
6775
6776
		// We do not want to use the imploded file in dev mode, or if not connected
6777
		if ( ( new Status() )->is_offline_mode() || ! self::is_active() ) {
6778
			if ( ! $travis_test ) {
6779
				return;
6780
			}
6781
		}
6782
6783
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6784
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6785
			return;
6786
		}
6787
6788
		/*
6789
		 * Now we assume Jetpack is connected and able to serve the single
6790
		 * file.
6791
		 *
6792
		 * In the future there will be a check here to serve the file locally
6793
		 * or potentially from the Jetpack CDN
6794
		 *
6795
		 * For now:
6796
		 * - Enqueue a single imploded css file
6797
		 * - Zero out the style_loader_tag for the bundled ones
6798
		 * - Be happy, drink scotch
6799
		 */
6800
6801
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6802
6803
		$version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6804
6805
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6806
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6807
	}
6808
6809
	function concat_remove_style_loader_tag( $tag, $handle ) {
6810
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6811
			$tag = '';
6812
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6813
				$tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6814
			}
6815
		}
6816
6817
		return $tag;
6818
	}
6819
6820
	/**
6821
	 * @deprecated
6822
	 * @see Automattic\Jetpack\Assets\add_aync_script
6823
	 */
6824
	public function script_add_async( $tag, $handle, $src ) {
6825
		_deprecated_function( __METHOD__, 'jetpack-8.6.0' );
6826
	}
6827
6828
	/*
6829
	 * Check the heartbeat data
6830
	 *
6831
	 * Organizes the heartbeat data by severity.  For example, if the site
6832
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6833
	 *
6834
	 * Data will be added to "caution" array, if it either:
6835
	 *  - Out of date Jetpack version
6836
	 *  - Out of date WP version
6837
	 *  - Out of date PHP version
6838
	 *
6839
	 * $return array $filtered_data
6840
	 */
6841
	public static function jetpack_check_heartbeat_data() {
6842
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6843
6844
		$good    = array();
6845
		$caution = array();
6846
		$bad     = array();
6847
6848
		foreach ( $raw_data as $stat => $value ) {
6849
6850
			// Check jetpack version
6851
			if ( 'version' == $stat ) {
6852
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6853
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__VERSION;
6854
					continue;
6855
				}
6856
			}
6857
6858
			// Check WP version
6859
			if ( 'wp-version' == $stat ) {
6860
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6861
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_WP_VERSION;
6862
					continue;
6863
				}
6864
			}
6865
6866
			// Check PHP version
6867
			if ( 'php-version' == $stat ) {
6868
				if ( version_compare( PHP_VERSION, JETPACK__MINIMUM_PHP_VERSION, '<' ) ) {
6869
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_PHP_VERSION;
6870
					continue;
6871
				}
6872
			}
6873
6874
			// Check ID crisis
6875
			if ( 'identitycrisis' == $stat ) {
6876
				if ( 'yes' == $value ) {
6877
					$bad[ $stat ] = $value;
6878
					continue;
6879
				}
6880
			}
6881
6882
			// The rest are good :)
6883
			$good[ $stat ] = $value;
6884
		}
6885
6886
		$filtered_data = array(
6887
			'good'    => $good,
6888
			'caution' => $caution,
6889
			'bad'     => $bad,
6890
		);
6891
6892
		return $filtered_data;
6893
	}
6894
6895
	/*
6896
	 * This method is used to organize all options that can be reset
6897
	 * without disconnecting Jetpack.
6898
	 *
6899
	 * It is used in class.jetpack-cli.php to reset options
6900
	 *
6901
	 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
6902
	 *
6903
	 * @return array of options to delete.
6904
	 */
6905
	public static function get_jetpack_options_for_reset() {
6906
		return Jetpack_Options::get_options_for_reset();
6907
	}
6908
6909
	/*
6910
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6911
	 * so we can bring them directly to their site in calypso.
6912
	 *
6913
	 * @deprecated 9.2.0 Use Automattic\Jetpack\Status::get_site_suffix
6914
	 *
6915
	 * @param string | url
6916
	 * @return string | url without the guff
6917
	 */
6918
	public static function build_raw_urls( $url ) {
6919
		_deprecated_function( __METHOD__, 'jetpack-9.2.0', 'Automattic\Jetpack\Status::get_site_suffix' );
6920
6921
		return ( new Status() )->get_site_suffix( $url );
6922
	}
6923
6924
	/**
6925
	 * Stores and prints out domains to prefetch for page speed optimization.
6926
	 *
6927
	 * @deprecated 8.8.0 Use Jetpack::add_resource_hints.
6928
	 *
6929
	 * @param string|array $urls URLs to hint.
0 ignored issues
show
Should the type for parameter $urls not be string|array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
6930
	 */
6931
	public static function dns_prefetch( $urls = null ) {
6932
		_deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Automattic\Jetpack\Assets::add_resource_hint' );
6933
		if ( $urls ) {
6934
			Assets::add_resource_hint( $urls );
6935
		}
6936
	}
6937
6938
	public function wp_dashboard_setup() {
6939
		if ( self::is_active() ) {
6940
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6941
		}
6942
6943
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6944
			$jetpack_logo = new Jetpack_Logo();
6945
			$widget_title = sprintf(
6946
				wp_kses(
6947
					/* translators: Placeholder is a Jetpack logo. */
6948
					__( 'Stats <span>by %s</span>', 'jetpack' ),
6949
					array( 'span' => array() )
6950
				),
6951
				$jetpack_logo->get_jp_emblem( true )
6952
			);
6953
6954
			wp_add_dashboard_widget(
6955
				'jetpack_summary_widget',
6956
				$widget_title,
6957
				array( __CLASS__, 'dashboard_widget' )
6958
			);
6959
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6960
			wp_style_add_data( 'jetpack-dashboard-widget', 'rtl', 'replace' );
6961
6962
			// If we're inactive and not in offline mode, sort our box to the top.
6963
			if ( ! self::is_active() && ! ( new Status() )->is_offline_mode() ) {
6964
				global $wp_meta_boxes;
6965
6966
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6967
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6968
6969
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6970
			}
6971
		}
6972
	}
6973
6974
	/**
6975
	 * @param mixed $result Value for the user's option
0 ignored issues
show
There is no parameter named $result. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6976
	 * @return mixed
6977
	 */
6978
	function get_user_option_meta_box_order_dashboard( $sorted ) {
6979
		if ( ! is_array( $sorted ) ) {
6980
			return $sorted;
6981
		}
6982
6983
		foreach ( $sorted as $box_context => $ids ) {
6984
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6985
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6986
				continue;
6987
			}
6988
6989
			$ids_array = explode( ',', $ids );
6990
			$key       = array_search( 'dashboard_stats', $ids_array );
6991
6992
			if ( false !== $key ) {
6993
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
6994
				$ids_array[ $key ]      = 'jetpack_summary_widget';
6995
				$sorted[ $box_context ] = implode( ',', $ids_array );
6996
				// We've found it, stop searching, and just return.
6997
				break;
6998
			}
6999
		}
7000
7001
		return $sorted;
7002
	}
7003
7004
	public static function dashboard_widget() {
7005
		/**
7006
		 * Fires when the dashboard is loaded.
7007
		 *
7008
		 * @since 3.4.0
7009
		 */
7010
		do_action( 'jetpack_dashboard_widget' );
7011
	}
7012
7013
	public static function dashboard_widget_footer() {
7014
		?>
7015
		<footer>
7016
7017
		<div class="protect">
7018
			<h3><?php esc_html_e( 'Brute force attack protection', 'jetpack' ); ?></h3>
7019
			<?php if ( self::is_module_active( 'protect' ) ) : ?>
7020
				<p class="blocked-count">
7021
					<?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?>
7022
				</p>
7023
				<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>
7024
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! ( new Status() )->is_offline_mode() ) : ?>
7025
				<a href="
7026
				<?php
7027
				echo esc_url(
7028
					wp_nonce_url(
7029
						self::admin_url(
7030
							array(
7031
								'action' => 'activate',
7032
								'module' => 'protect',
7033
							)
7034
						),
7035
						'jetpack_activate-protect'
7036
					)
7037
				);
7038
				?>
7039
							" class="button button-jetpack" title="<?php esc_attr_e( 'Protect helps to keep you secure from brute-force login attacks.', 'jetpack' ); ?>">
7040
					<?php esc_html_e( 'Activate brute force attack protection', 'jetpack' ); ?>
7041
				</a>
7042
			<?php else : ?>
7043
				<?php esc_html_e( 'Brute force attack protection is inactive.', 'jetpack' ); ?>
7044
			<?php endif; ?>
7045
		</div>
7046
7047
		<div class="akismet">
7048
			<h3><?php esc_html_e( 'Anti-spam', 'jetpack' ); ?></h3>
7049
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
7050
				<p class="blocked-count">
7051
					<?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?>
7052
				</p>
7053
				<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>
7054
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
7055
				<a href="
7056
				<?php
7057
				echo esc_url(
7058
					wp_nonce_url(
7059
						add_query_arg(
7060
							array(
7061
								'action' => 'activate',
7062
								'plugin' => 'akismet/akismet.php',
7063
							),
7064
							admin_url( 'plugins.php' )
7065
						),
7066
						'activate-plugin_akismet/akismet.php'
7067
					)
7068
				);
7069
				?>
7070
							" class="button button-jetpack">
7071
					<?php esc_html_e( 'Activate Anti-spam', 'jetpack' ); ?>
7072
				</a>
7073
			<?php else : ?>
7074
				<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>
7075
			<?php endif; ?>
7076
		</div>
7077
7078
		</footer>
7079
		<?php
7080
	}
7081
7082
	/*
7083
	 * Adds a "blank" column in the user admin table to display indication of user connection.
7084
	 */
7085
	function jetpack_icon_user_connected( $columns ) {
7086
		$columns['user_jetpack'] = '';
7087
		return $columns;
7088
	}
7089
7090
	/*
7091
	 * Show Jetpack icon if the user is linked.
7092
	 */
7093
	function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
7094
		if ( 'user_jetpack' == $col && self::is_user_connected( $user_id ) ) {
7095
			$jetpack_logo = new Jetpack_Logo();
7096
			$emblem_html  = sprintf(
7097
				'<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
7098
				esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
7099
				$jetpack_logo->get_jp_emblem()
7100
			);
7101
			return $emblem_html;
7102
		}
7103
7104
		return $val;
7105
	}
7106
7107
	/*
7108
	 * Style the Jetpack user column
7109
	 */
7110
	function jetpack_user_col_style() {
7111
		global $current_screen;
7112
		if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) {
7113
			?>
7114
			<style>
7115
				.fixed .column-user_jetpack {
7116
					width: 21px;
7117
				}
7118
				.jp-emblem-user-admin svg {
7119
					width: 20px;
7120
					height: 20px;
7121
				}
7122
				.jp-emblem-user-admin path {
7123
					fill: #00BE28;
7124
				}
7125
			</style>
7126
			<?php
7127
		}
7128
	}
7129
7130
	/**
7131
	 * Checks if Akismet is active and working.
7132
	 *
7133
	 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
7134
	 * that implied usage of methods present since more recent version.
7135
	 * See https://github.com/Automattic/jetpack/pull/9585
7136
	 *
7137
	 * @since  5.1.0
7138
	 *
7139
	 * @return bool True = Akismet available. False = Aksimet not available.
7140
	 */
7141
	public static function is_akismet_active() {
7142
		static $status = null;
7143
7144
		if ( ! is_null( $status ) ) {
7145
			return $status;
7146
		}
7147
7148
		// Check if a modern version of Akismet is active.
7149
		if ( ! method_exists( 'Akismet', 'http_post' ) ) {
7150
			$status = false;
7151
			return $status;
7152
		}
7153
7154
		// Make sure there is a key known to Akismet at all before verifying key.
7155
		$akismet_key = Akismet::get_api_key();
7156
		if ( ! $akismet_key ) {
7157
			$status = false;
7158
			return $status;
7159
		}
7160
7161
		// Possible values: valid, invalid, failure via Akismet. false if no status is cached.
7162
		$akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
7163
7164
		// 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.
7165
		$recheck = ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state;
7166
		// We cache the result of the Akismet key verification for ten minutes.
7167
		if ( ! $akismet_key_state || $recheck ) {
7168
			$akismet_key_state = Akismet::verify_key( $akismet_key );
7169
			set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
7170
		}
7171
7172
		$status = 'valid' === $akismet_key_state;
7173
7174
		return $status;
7175
	}
7176
7177
	/**
7178
	 * @deprecated
7179
	 *
7180
	 * @see Automattic\Jetpack\Sync\Modules\Users::is_function_in_backtrace
7181
	 */
7182
	public static function is_function_in_backtrace() {
7183
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
7184
	}
7185
7186
	/**
7187
	 * Given a minified path, and a non-minified path, will return
7188
	 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
7189
	 *
7190
	 * Both `$min_base` and `$non_min_base` are expected to be relative to the
7191
	 * root Jetpack directory.
7192
	 *
7193
	 * @since 5.6.0
7194
	 *
7195
	 * @param string $min_path
7196
	 * @param string $non_min_path
7197
	 * @return string The URL to the file
7198
	 */
7199
	public static function get_file_url_for_environment( $min_path, $non_min_path ) {
7200
		return Assets::get_file_url_for_environment( $min_path, $non_min_path );
7201
	}
7202
7203
	/**
7204
	 * Checks for whether Jetpack Backup is enabled.
7205
	 * Will return true if the state of Backup is anything except "unavailable".
7206
	 *
7207
	 * @return bool|int|mixed
7208
	 */
7209
	public static function is_rewind_enabled() {
7210
		if ( ! self::is_active() ) {
7211
			return false;
7212
		}
7213
7214
		$rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7215
		if ( false === $rewind_enabled ) {
7216
			jetpack_require_lib( 'class.core-rest-api-endpoints' );
7217
			$rewind_data    = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7218
			$rewind_enabled = ( ! is_wp_error( $rewind_data )
7219
				&& ! empty( $rewind_data['state'] )
7220
				&& 'active' === $rewind_data['state'] )
7221
				? 1
7222
				: 0;
7223
7224
			set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7225
		}
7226
		return $rewind_enabled;
7227
	}
7228
7229
	/**
7230
	 * Return Calypso environment value; used for developing Jetpack and pairing
7231
	 * it with different Calypso enrionments, such as localhost.
7232
	 *
7233
	 * @since 7.4.0
7234
	 *
7235
	 * @return string Calypso environment
7236
	 */
7237
	public static function get_calypso_env() {
7238
		if ( isset( $_GET['calypso_env'] ) ) {
7239
			return sanitize_key( $_GET['calypso_env'] );
7240
		}
7241
7242
		if ( getenv( 'CALYPSO_ENV' ) ) {
7243
			return sanitize_key( getenv( 'CALYPSO_ENV' ) );
7244
		}
7245
7246
		if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
7247
			return sanitize_key( CALYPSO_ENV );
7248
		}
7249
7250
		return '';
7251
	}
7252
7253
	/**
7254
	 * Returns the hostname with protocol for Calypso.
7255
	 * Used for developing Jetpack with Calypso.
7256
	 *
7257
	 * @since 8.4.0
7258
	 *
7259
	 * @return string Calypso host.
7260
	 */
7261
	public static function get_calypso_host() {
7262
		$calypso_env = self::get_calypso_env();
7263
		switch ( $calypso_env ) {
7264
			case 'development':
7265
				return 'http://calypso.localhost:3000/';
7266
			case 'wpcalypso':
7267
				return 'https://wpcalypso.wordpress.com/';
7268
			case 'horizon':
7269
				return 'https://horizon.wordpress.com/';
7270
			default:
7271
				return 'https://wordpress.com/';
7272
		}
7273
	}
7274
7275
	/**
7276
	 * Handles activating default modules as well general cleanup for the new connection.
7277
	 *
7278
	 * @param boolean $activate_sso                 Whether to activate the SSO module when activating default modules.
7279
	 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7280
	 * @param boolean $send_state_messages          Whether to send state messages.
7281
	 * @return void
7282
	 */
7283
	public static function handle_post_authorization_actions(
7284
		$activate_sso = false,
7285
		$redirect_on_activation_error = false,
7286
		$send_state_messages = true
7287
	) {
7288
		$other_modules = $activate_sso
7289
			? array( 'sso' )
7290
			: array();
7291
7292
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
7293
			self::delete_active_modules();
7294
7295
			self::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, $send_state_messages );
0 ignored issues
show
999 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
7296
		} else {
7297
			self::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages );
7298
		}
7299
7300
		// Since this is a fresh connection, be sure to clear out IDC options
7301
		Jetpack_IDC::clear_all_idc_options();
7302
7303
		if ( $send_state_messages ) {
7304
			self::state( 'message', 'authorized' );
7305
		}
7306
	}
7307
7308
	/**
7309
	 * Returns a boolean for whether backups UI should be displayed or not.
7310
	 *
7311
	 * @return bool Should backups UI be displayed?
7312
	 */
7313
	public static function show_backups_ui() {
7314
		/**
7315
		 * Whether UI for backups should be displayed.
7316
		 *
7317
		 * @since 6.5.0
7318
		 *
7319
		 * @param bool $show_backups Should UI for backups be displayed? True by default.
7320
		 */
7321
		return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7322
	}
7323
7324
	/*
7325
	 * Deprecated manage functions
7326
	 */
7327
	function prepare_manage_jetpack_notice() {
7328
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7329
	}
7330
	function manage_activate_screen() {
7331
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7332
	}
7333
	function admin_jetpack_manage_notice() {
7334
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7335
	}
7336
	function opt_out_jetpack_manage_url() {
7337
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7338
	}
7339
	function opt_in_jetpack_manage_url() {
7340
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7341
	}
7342
	function opt_in_jetpack_manage_notice() {
7343
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7344
	}
7345
	function can_display_jetpack_manage_notice() {
7346
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7347
	}
7348
7349
	/**
7350
	 * Clean leftoveruser meta.
7351
	 *
7352
	 * Delete Jetpack-related user meta when it is no longer needed.
7353
	 *
7354
	 * @since 7.3.0
7355
	 *
7356
	 * @param int $user_id User ID being updated.
7357
	 */
7358
	public static function user_meta_cleanup( $user_id ) {
7359
		$meta_keys = array(
7360
			// AtD removed from Jetpack 7.3
7361
			'AtD_options',
7362
			'AtD_check_when',
7363
			'AtD_guess_lang',
7364
			'AtD_ignored_phrases',
7365
		);
7366
7367
		foreach ( $meta_keys as $meta_key ) {
7368
			if ( get_user_meta( $user_id, $meta_key ) ) {
7369
				delete_user_meta( $user_id, $meta_key );
7370
			}
7371
		}
7372
	}
7373
7374
	/**
7375
	 * Checks if a Jetpack site is both active and not in offline mode.
7376
	 *
7377
	 * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_offline_mode`.
7378
	 *
7379
	 * @deprecated 8.8.0
7380
	 *
7381
	 * @return bool True if Jetpack is active and not in offline mode.
7382
	 */
7383
	public static function is_active_and_not_development_mode() {
7384
		_deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Jetpack::is_active_and_not_offline_mode' );
7385
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() ) {
7386
			return false;
7387
		}
7388
		return true;
7389
	}
7390
7391
	/**
7392
	 * Checks if a Jetpack site is both active and not in offline mode.
7393
	 *
7394
	 * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_offline_mode`.
7395
	 *
7396
	 * @since 8.8.0
7397
	 *
7398
	 * @return bool True if Jetpack is active and not in offline mode.
7399
	 */
7400
	public static function is_active_and_not_offline_mode() {
7401
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() ) {
7402
			return false;
7403
		}
7404
		return true;
7405
	}
7406
7407
	/**
7408
	 * Returns the list of products that we have available for purchase.
7409
	 */
7410
	public static function get_products_for_purchase() {
7411
		$products = array();
7412
		if ( ! is_multisite() ) {
7413
			$products[] = array(
7414
				'key'               => 'backup',
7415
				'title'             => __( 'Jetpack Backup', 'jetpack' ),
7416
				'short_description' => __( 'Always-on backups ensure you never lose your site.', 'jetpack' ),
7417
				'learn_more'        => __( 'Which backup option is best for me?', 'jetpack' ),
7418
				'description'       => __( 'Always-on backups ensure you never lose your site. Your changes are saved as you edit and you have unlimited backup archives.', 'jetpack' ),
7419
				'options_label'     => __( 'Select a backup option:', 'jetpack' ),
7420
				'options'           => array(
7421
					array(
7422
						'type'        => 'daily',
7423
						'slug'        => 'jetpack-backup-daily',
7424
						'key'         => 'jetpack_backup_daily',
7425
						'name'        => __( 'Daily Backups', 'jetpack' ),
7426
						'description' => __( 'Your data is being securely backed up daily.', 'jetpack' ),
7427
					),
7428
					array(
7429
						'type'        => 'realtime',
7430
						'slug'        => 'jetpack-backup-realtime',
7431
						'key'         => 'jetpack_backup_realtime',
7432
						'name'        => __( 'Real-Time Backups', 'jetpack' ),
7433
						'description' => __( 'Your data is being securely backed up as you edit.', 'jetpack' ),
7434
					),
7435
				),
7436
				'default_option'    => 'realtime',
7437
				'show_promotion'    => true,
7438
				'discount_percent'  => 70,
7439
				'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'daily-backup-plan', 'realtime-backup-plan' ),
7440
			);
7441
7442
			$products[] = array(
7443
				'key'               => 'scan',
7444
				'title'             => __( 'Jetpack Scan', 'jetpack' ),
7445
				'short_description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7446
				'learn_more'        => __( 'Learn More', 'jetpack' ),
7447
				'description'       => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7448
				'show_promotion'    => true,
7449
				'discount_percent'  => 30,
7450
				'options'           => array(
7451
					array(
7452
						'type' => 'scan',
7453
						'slug' => 'jetpack-scan',
7454
						'key'  => 'jetpack_scan',
7455
						'name' => __( 'Daily Scan', 'jetpack' ),
7456
					),
7457
				),
7458
				'default_option'    => 'scan',
7459
				'included_in_plans' => array( 'premium-plan', 'business-plan', 'scan-plan' ),
7460
			);
7461
		}
7462
7463
		$products[] = array(
7464
			'key'               => 'search',
7465
			'title'             => __( 'Jetpack Search', 'jetpack' ),
7466
			'short_description' => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7467
			'learn_more'        => __( 'Learn More', 'jetpack' ),
7468
			'description'       => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7469
			'label_popup'       => __( 'Records are all posts, pages, custom post types, and other types of content indexed by Jetpack Search.', 'jetpack' ),
7470
			'options'           => array(
7471
				array(
7472
					'type' => 'search',
7473
					'slug' => 'jetpack-search',
7474
					'key'  => 'jetpack_search',
7475
					'name' => __( 'Search', 'jetpack' ),
7476
				),
7477
			),
7478
			'tears'             => array(),
7479
			'default_option'    => 'search',
7480
			'show_promotion'    => false,
7481
			'included_in_plans' => array( 'search-plan' ),
7482
		);
7483
7484
		$products[] = array(
7485
			'key'               => 'anti-spam',
7486
			'title'             => __( 'Jetpack Anti-Spam', 'jetpack' ),
7487
			'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' ),
7488
			'learn_more'        => __( 'Learn More', 'jetpack' ),
7489
			'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' ),
7490
			'options'           => array(
7491
				array(
7492
					'type' => 'anti-spam',
7493
					'slug' => 'jetpack-anti-spam',
7494
					'key'  => 'jetpack_anti_spam',
7495
					'name' => __( 'Anti-Spam', 'jetpack' ),
7496
				),
7497
			),
7498
			'default_option'    => 'anti-spam',
7499
			'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'anti-spam-plan' ),
7500
		);
7501
7502
		return $products;
7503
	}
7504
}
7505