Completed
Push — add/success-message-on-partial... ( 54110c...e1e50b )
by
unknown
26:07 queued 17:48
created

class.jetpack.php (83 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\Utils as Connection_Utils;
8
use Automattic\Jetpack\Connection\Plugin_Storage as Connection_Plugin_Storage;
9
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
10
use Automattic\Jetpack\Constants;
11
use Automattic\Jetpack\Partner;
12
use Automattic\Jetpack\Roles;
13
use Automattic\Jetpack\Status;
14
use Automattic\Jetpack\Sync\Functions;
15
use Automattic\Jetpack\Sync\Health;
16
use Automattic\Jetpack\Sync\Sender;
17
use Automattic\Jetpack\Sync\Users;
18
use Automattic\Jetpack\Terms_Of_Service;
19
use Automattic\Jetpack\Tracking;
20
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
21
use Automattic\Jetpack\Redirect;
22
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
23
24
/*
25
Options:
26
jetpack_options (array)
27
	An array of options.
28
	@see Jetpack_Options::get_option_names()
29
30
jetpack_register (string)
31
	Temporary verification secrets.
32
33
jetpack_activated (int)
34
	1: the plugin was activated normally
35
	2: the plugin was activated on this site because of a network-wide activation
36
	3: the plugin was auto-installed
37
	4: the plugin was manually disconnected (but is still installed)
38
39
jetpack_active_modules (array)
40
	Array of active module slugs.
41
42
jetpack_do_activate (bool)
43
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
44
*/
45
46
require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php';
47
48
class Jetpack {
49
	public $xmlrpc_server = null;
50
51
	/**
52
	 * @var array The handles of styles that are concatenated into jetpack.css.
53
	 *
54
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
55
	 */
56
	public $concatenated_style_handles = array(
57
		'jetpack-carousel',
58
		'grunion.css',
59
		'the-neverending-homepage',
60
		'jetpack_likes',
61
		'jetpack_related-posts',
62
		'sharedaddy',
63
		'jetpack-slideshow',
64
		'presentations',
65
		'quiz',
66
		'jetpack-subscriptions',
67
		'jetpack-responsive-videos-style',
68
		'jetpack-social-menu',
69
		'tiled-gallery',
70
		'jetpack_display_posts_widget',
71
		'gravatar-profile-widget',
72
		'goodreads-widget',
73
		'jetpack_social_media_icons_widget',
74
		'jetpack-top-posts-widget',
75
		'jetpack_image_widget',
76
		'jetpack-my-community-widget',
77
		'jetpack-authors-widget',
78
		'wordads',
79
		'eu-cookie-law-style',
80
		'flickr-widget-style',
81
		'jetpack-search-widget',
82
		'jetpack-simple-payments-widget-style',
83
		'jetpack-widget-social-icons-styles',
84
		'wpcom_instagram_widget',
85
	);
86
87
	/**
88
	 * Contains all assets that have had their URL rewritten to minified versions.
89
	 *
90
	 * @var array
91
	 */
92
	static $min_assets = array();
93
94
	public $plugins_to_deactivate = array(
95
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
96
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
97
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
98
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
99
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
100
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
101
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
102
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
103
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
104
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
105
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
106
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
107
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
108
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' ),
109
	);
110
111
	/**
112
	 * Map of roles we care about, and their corresponding minimum capabilities.
113
	 *
114
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::$capability_translations instead.
115
	 *
116
	 * @access public
117
	 * @static
118
	 *
119
	 * @var array
120
	 */
121
	public static $capability_translations = array(
122
		'administrator' => 'manage_options',
123
		'editor'        => 'edit_others_posts',
124
		'author'        => 'publish_posts',
125
		'contributor'   => 'edit_posts',
126
		'subscriber'    => 'read',
127
	);
128
129
	/**
130
	 * Map of modules that have conflicts with plugins and should not be auto-activated
131
	 * if the plugins are active.  Used by filter_default_modules
132
	 *
133
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
134
	 * change `module-slug` and add this to your plugin:
135
	 *
136
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
137
	 * function my_jetpack_get_default_modules( $modules ) {
138
	 *     return array_diff( $modules, array( 'module-slug' ) );
139
	 * }
140
	 *
141
	 * @var array
142
	 */
143
	private $conflicting_plugins = array(
144
		'comments'           => array(
145
			'Intense Debate'                 => 'intensedebate/intensedebate.php',
146
			'Disqus'                         => 'disqus-comment-system/disqus.php',
147
			'Livefyre'                       => 'livefyre-comments/livefyre.php',
148
			'Comments Evolved for WordPress' => 'gplus-comments/comments-evolved.php',
149
			'Google+ Comments'               => 'google-plus-comments/google-plus-comments.php',
150
			'WP-SpamShield Anti-Spam'        => 'wp-spamshield/wp-spamshield.php',
151
		),
152
		'comment-likes'      => array(
153
			'Epoch' => 'epoch/plugincore.php',
154
		),
155
		'contact-form'       => array(
156
			'Contact Form 7'           => 'contact-form-7/wp-contact-form-7.php',
157
			'Gravity Forms'            => 'gravityforms/gravityforms.php',
158
			'Contact Form Plugin'      => 'contact-form-plugin/contact_form.php',
159
			'Easy Contact Forms'       => 'easy-contact-forms/easy-contact-forms.php',
160
			'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php',
161
			'Ninja Forms'              => 'ninja-forms/ninja-forms.php',
162
		),
163
		'latex'              => array(
164
			'LaTeX for WordPress'     => 'latex/latex.php',
165
			'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php',
166
			'Easy WP LaTeX'           => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
167
			'MathJax-LaTeX'           => 'mathjax-latex/mathjax-latex.php',
168
			'Enable Latex'            => 'enable-latex/enable-latex.php',
169
			'WP QuickLaTeX'           => 'wp-quicklatex/wp-quicklatex.php',
170
		),
171
		'protect'            => array(
172
			'Limit Login Attempts'              => 'limit-login-attempts/limit-login-attempts.php',
173
			'Captcha'                           => 'captcha/captcha.php',
174
			'Brute Force Login Protection'      => 'brute-force-login-protection/brute-force-login-protection.php',
175
			'Login Security Solution'           => 'login-security-solution/login-security-solution.php',
176
			'WPSecureOps Brute Force Protect'   => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
177
			'BulletProof Security'              => 'bulletproof-security/bulletproof-security.php',
178
			'SiteGuard WP Plugin'               => 'siteguard/siteguard.php',
179
			'Security-protection'               => 'security-protection/security-protection.php',
180
			'Login Security'                    => 'login-security/login-security.php',
181
			'Botnet Attack Blocker'             => 'botnet-attack-blocker/botnet-attack-blocker.php',
182
			'Wordfence Security'                => 'wordfence/wordfence.php',
183
			'All In One WP Security & Firewall' => 'all-in-one-wp-security-and-firewall/wp-security.php',
184
			'iThemes Security'                  => 'better-wp-security/better-wp-security.php',
185
		),
186
		'random-redirect'    => array(
187
			'Random Redirect 2' => 'random-redirect-2/random-redirect.php',
188
		),
189
		'related-posts'      => array(
190
			'YARPP'                       => 'yet-another-related-posts-plugin/yarpp.php',
191
			'WordPress Related Posts'     => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
192
			'nrelate Related Content'     => 'nrelate-related-content/nrelate-related.php',
193
			'Contextual Related Posts'    => 'contextual-related-posts/contextual-related-posts.php',
194
			'Related Posts for WordPress' => 'microkids-related-posts/microkids-related-posts.php',
195
			'outbrain'                    => 'outbrain/outbrain.php',
196
			'Shareaholic'                 => 'shareaholic/shareaholic.php',
197
			'Sexybookmarks'               => 'sexybookmarks/shareaholic.php',
198
		),
199
		'sharedaddy'         => array(
200
			'AddThis'     => 'addthis/addthis_social_widget.php',
201
			'Add To Any'  => 'add-to-any/add-to-any.php',
202
			'ShareThis'   => 'share-this/sharethis.php',
203
			'Shareaholic' => 'shareaholic/shareaholic.php',
204
		),
205
		'seo-tools'          => array(
206
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
207
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
208
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
209
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
210
			'The SEO Framework'              => 'autodescription/autodescription.php',
211
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
212
			'Slim SEO'                       => 'slim-seo/slim-seo.php',
213
		),
214
		'verification-tools' => array(
215
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
216
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
217
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
218
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
219
			'The SEO Framework'              => 'autodescription/autodescription.php',
220
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
221
			'Slim SEO'                       => 'slim-seo/slim-seo.php',
222
		),
223
		'widget-visibility'  => array(
224
			'Widget Logic'    => 'widget-logic/widget_logic.php',
225
			'Dynamic Widgets' => 'dynamic-widgets/dynamic-widgets.php',
226
		),
227
		'sitemaps'           => array(
228
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
229
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
230
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
231
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
232
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
233
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
234
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
235
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
236
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
237
			'The SEO Framework'                    => 'autodescription/autodescription.php',
238
			'Sitemap'                              => 'sitemap/sitemap.php',
239
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
240
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
241
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
242
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
243
			'Rank Math'                            => 'seo-by-rank-math/rank-math.php',
244
			'Slim SEO'                             => 'slim-seo/slim-seo.php',
245
		),
246
		'lazy-images'        => array(
247
			'Lazy Load'              => 'lazy-load/lazy-load.php',
248
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
249
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
250
		),
251
	);
252
253
	/**
254
	 * Plugins for which we turn off our Facebook OG Tags implementation.
255
	 *
256
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
257
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
258
	 *
259
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
260
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
261
	 */
262
	private $open_graph_conflicting_plugins = array(
263
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
264
		// 2 Click Social Media Buttons
265
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
266
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
267
		'complete-open-graph/complete-open-graph.php',           // Complete Open Graph
268
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
269
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
270
		// Open Graph Meta Tags by Heateor
271
		'facebook/facebook.php',                                 // Facebook (official plugin)
272
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
273
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
274
		// Facebook Featured Image & OG Meta Tags
275
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
276
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
277
		// Facebook Open Graph Meta Tags for WordPress
278
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
279
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
280
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
281
		// Fedmich's Facebook Open Graph Meta
282
		'network-publisher/networkpub.php',                      // Network Publisher
283
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
284
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
285
		// NextScripts SNAP
286
		'og-tags/og-tags.php',                                   // OG Tags
287
		'opengraph/opengraph.php',                               // Open Graph
288
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
289
		// Open Graph Protocol Framework
290
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
291
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
292
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
293
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
294
		'sharepress/sharepress.php',                             // SharePress
295
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
296
		'social-discussions/social-discussions.php',             // Social Discussions
297
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
298
		'socialize/socialize.php',                               // Socialize
299
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
300
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
301
		// Tweet, Like, Google +1 and Share
302
		'wordbooker/wordbooker.php',                             // Wordbooker
303
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
304
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
305
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
306
		// WP Facebook Like Send & Open Graph Meta
307
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
308
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
309
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
310
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
311
		'open-graph-metabox/open-graph-metabox.php',              // Open Graph Metabox
312
		'seo-by-rank-math/rank-math.php',                        // Rank Math.
313
		'slim-seo/slim-seo.php',                                 // Slim SEO
314
	);
315
316
	/**
317
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
318
	 */
319
	private $twitter_cards_conflicting_plugins = array(
320
		// 'twitter/twitter.php',                       // The official one handles this on its own.
321
		// https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
322
			'eewee-twitter-card/index.php',              // Eewee Twitter Card
323
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
324
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
325
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
326
		// Pure Web Brilliant's Social Graph Twitter Cards Extension
327
		'twitter-cards/twitter-cards.php',           // Twitter Cards
328
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
329
		'wp-to-twitter/wp-to-twitter.php',           // WP to Twitter
330
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
331
		'seo-by-rank-math/rank-math.php',            // Rank Math.
332
		'slim-seo/slim-seo.php',                     // Slim SEO
333
	);
334
335
	/**
336
	 * Message to display in admin_notice
337
	 *
338
	 * @var string
339
	 */
340
	public $message = '';
341
342
	/**
343
	 * Error to display in admin_notice
344
	 *
345
	 * @var string
346
	 */
347
	public $error = '';
348
349
	/**
350
	 * Modules that need more privacy description.
351
	 *
352
	 * @var string
353
	 */
354
	public $privacy_checks = '';
355
356
	/**
357
	 * Stats to record once the page loads
358
	 *
359
	 * @var array
360
	 */
361
	public $stats = array();
362
363
	/**
364
	 * Jetpack_Sync object
365
	 */
366
	public $sync;
367
368
	/**
369
	 * Verified data for JSON authorization request
370
	 */
371
	public $json_api_authorization_request = array();
372
373
	/**
374
	 * @var Automattic\Jetpack\Connection\Manager
375
	 */
376
	protected $connection_manager;
377
378
	/**
379
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
380
	 */
381
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
382
383
	/**
384
	 * Holds an instance of Automattic\Jetpack\A8c_Mc_Stats
385
	 *
386
	 * @var Automattic\Jetpack\A8c_Mc_Stats
387
	 */
388
	public $a8c_mc_stats_instance;
389
390
	/**
391
	 * Constant for login redirect key.
392
	 *
393
	 * @var string
394
	 * @since 8.4.0
395
	 */
396
	public static $jetpack_redirect_login = 'jetpack_connect_login_redirect';
397
398
	/**
399
	 * Holds the singleton instance of this class
400
	 *
401
	 * @since 2.3.3
402
	 * @var Jetpack
403
	 */
404
	static $instance = false;
405
406
	/**
407
	 * Singleton
408
	 *
409
	 * @static
410
	 */
411
	public static function init() {
412
		if ( ! self::$instance ) {
413
			self::$instance = new Jetpack();
414
			add_action( 'plugins_loaded', array( self::$instance, 'plugin_upgrade' ) );
415
		}
416
417
		return self::$instance;
418
	}
419
420
	/**
421
	 * Must never be called statically
422
	 */
423
	function plugin_upgrade() {
424
		if ( self::is_active() ) {
425
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
426
			if ( JETPACK__VERSION != $version ) {
427
				// Prevent multiple upgrades at once - only a single process should trigger
428
				// an upgrade to avoid stampedes
429
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
430
					return;
431
				}
432
433
				// Set a short lock to prevent multiple instances of the upgrade
434
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
435
436
				// check which active modules actually exist and remove others from active_modules list
437
				$unfiltered_modules = self::get_active_modules();
438
				$modules            = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
439
				if ( array_diff( $unfiltered_modules, $modules ) ) {
440
					self::update_active_modules( $modules );
441
				}
442
443
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
444
445
				// Upgrade to 4.3.0
446
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
447
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
448
				}
449
450
				// Make sure Markdown for posts gets turned back on
451
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
452
					update_option( 'wpcom_publish_posts_with_markdown', true );
453
				}
454
455
				/*
456
				 * Minileven deprecation. 8.3.0.
457
				 * Only delete options if not using
458
				 * the replacement standalone Minileven plugin.
459
				 */
460
				if (
461
					! self::is_plugin_active( 'minileven-master/minileven.php' )
462
					&& ! self::is_plugin_active( 'minileven/minileven.php' )
463
				) {
464
					if ( get_option( 'wp_mobile_custom_css' ) ) {
465
						delete_option( 'wp_mobile_custom_css' );
466
					}
467
					if ( get_option( 'wp_mobile_excerpt' ) ) {
468
						delete_option( 'wp_mobile_excerpt' );
469
					}
470
					if ( get_option( 'wp_mobile_featured_images' ) ) {
471
						delete_option( 'wp_mobile_featured_images' );
472
					}
473
					if ( get_option( 'wp_mobile_app_promos' ) ) {
474
						delete_option( 'wp_mobile_app_promos' );
475
					}
476
				}
477
478
				// Upgrade to 8.4.0.
479
				if ( Jetpack_Options::get_option( 'ab_connect_banner_green_bar' ) ) {
480
					Jetpack_Options::delete_option( 'ab_connect_banner_green_bar' );
481
				}
482
483
				// Update to 8.8.x (WordPress 5.5 Compatibility).
484
				if ( Jetpack_Options::get_option( 'autoupdate_plugins' ) ) {
485
					$updated = update_site_option(
486
						'auto_update_plugins',
487
						array_unique(
488
							array_merge(
489
								(array) Jetpack_Options::get_option( 'autoupdate_plugins', array() ),
490
								(array) get_site_option( 'auto_update_plugins', array() )
491
							)
492
						)
493
					);
494
495
					if ( $updated ) {
496
						Jetpack_Options::delete_option( 'autoupdate_plugins' );
497
					} // Should we have some type of fallback if something fails here?
498
				}
499
500
				if ( did_action( 'wp_loaded' ) ) {
501
					self::upgrade_on_load();
502
				} else {
503
					add_action(
504
						'wp_loaded',
505
						array( __CLASS__, 'upgrade_on_load' )
506
					);
507
				}
508
			}
509
		}
510
	}
511
512
	/**
513
	 * Runs upgrade routines that need to have modules loaded.
514
	 */
515
	static function upgrade_on_load() {
516
517
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
518
		// This can happen in case Jetpack has been just upgraded and is
519
		// being initialized late during the page load. In this case we wait
520
		// until the next proper admin page load with Jetpack active.
521
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
522
			delete_transient( self::$plugin_upgrade_lock_key );
523
524
			return;
525
		}
526
527
		self::maybe_set_version_option();
528
529
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
530
			Jetpack_Widget_Conditions::migrate_post_type_rules();
531
		}
532
533
		if (
534
			class_exists( 'Jetpack_Sitemap_Manager' )
535
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
536
		) {
537
			do_action( 'jetpack_sitemaps_purge_data' );
538
		}
539
540
		// Delete old stats cache
541
		delete_option( 'jetpack_restapi_stats_cache' );
542
543
		delete_transient( self::$plugin_upgrade_lock_key );
544
	}
545
546
	/**
547
	 * Saves all the currently active modules to options.
548
	 * Also fires Action hooks for each newly activated and deactivated module.
549
	 *
550
	 * @param $modules Array Array of active modules to be saved in options.
551
	 *
552
	 * @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...
553
	 */
554
	static function update_active_modules( $modules ) {
555
		$current_modules      = Jetpack_Options::get_option( 'active_modules', array() );
556
		$active_modules       = self::get_active_modules();
557
		$new_active_modules   = array_diff( $modules, $current_modules );
558
		$new_inactive_modules = array_diff( $active_modules, $modules );
559
		$new_current_modules  = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
560
		$reindexed_modules    = array_values( $new_current_modules );
561
		$success              = Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
562
563
		foreach ( $new_active_modules as $module ) {
564
			/**
565
			 * Fires when a specific module is activated.
566
			 *
567
			 * @since 1.9.0
568
			 *
569
			 * @param string $module Module slug.
570
			 * @param boolean $success whether the module was activated. @since 4.2
571
			 */
572
			do_action( 'jetpack_activate_module', $module, $success );
573
			/**
574
			 * Fires when a module is activated.
575
			 * The dynamic part of the filter, $module, is the module slug.
576
			 *
577
			 * @since 1.9.0
578
			 *
579
			 * @param string $module Module slug.
580
			 */
581
			do_action( "jetpack_activate_module_$module", $module );
582
		}
583
584
		foreach ( $new_inactive_modules as $module ) {
585
			/**
586
			 * Fired after a module has been deactivated.
587
			 *
588
			 * @since 4.2.0
589
			 *
590
			 * @param string $module Module slug.
591
			 * @param boolean $success whether the module was deactivated.
592
			 */
593
			do_action( 'jetpack_deactivate_module', $module, $success );
594
			/**
595
			 * Fires when a module is deactivated.
596
			 * The dynamic part of the filter, $module, is the module slug.
597
			 *
598
			 * @since 1.9.0
599
			 *
600
			 * @param string $module Module slug.
601
			 */
602
			do_action( "jetpack_deactivate_module_$module", $module );
603
		}
604
605
		return $success;
606
	}
607
608
	static function delete_active_modules() {
609
		self::update_active_modules( array() );
610
	}
611
612
	/**
613
	 * Adds a hook to plugins_loaded at a priority that's currently the earliest
614
	 * available.
615
	 */
616
	public function add_configure_hook() {
617
		global $wp_filter;
618
619
		$current_priority = has_filter( 'plugins_loaded', array( $this, 'configure' ) );
620
		if ( false !== $current_priority ) {
621
			remove_action( 'plugins_loaded', array( $this, 'configure' ), $current_priority );
622
		}
623
624
		$taken_priorities = array_map( 'intval', array_keys( $wp_filter['plugins_loaded']->callbacks ) );
625
		sort( $taken_priorities );
626
627
		$first_priority = array_shift( $taken_priorities );
628
629
		if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
630
			$new_priority = - PHP_INT_MAX;
631
		} else {
632
			$new_priority = $first_priority - 1;
633
		}
634
635
		add_action( 'plugins_loaded', array( $this, 'configure' ), $new_priority );
636
	}
637
638
	/**
639
	 * Constructor.  Initializes WordPress hooks
640
	 */
641
	private function __construct() {
642
		/*
643
		 * Check for and alert any deprecated hooks
644
		 */
645
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
646
647
		// Note how this runs at an earlier plugin_loaded hook intentionally to accomodate for other plugins.
648
		add_action( 'plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
649
		add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
650
		add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
651
		add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
652
653
		add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
654
655
		add_filter(
656
			'jetpack_signature_check_token',
657
			array( __CLASS__, 'verify_onboarding_token' ),
658
			10,
659
			3
660
		);
661
662
		/**
663
		 * Prepare Gutenberg Editor functionality
664
		 */
665
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
666
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
667
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
668
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_extended_blocks' ), 9 );
669
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
670
671
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
672
673
		// Unlink user before deleting the user from WP.com.
674
		add_action( 'deleted_user', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
675
		add_action( 'remove_user_from_blog', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
676
677
		add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
678
679
		add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
680
		add_action( 'login_init', array( $this, 'login_init' ) );
681
682
		// Set up the REST authentication hooks.
683
		Connection_Rest_Authentication::init();
684
685
		add_action( 'admin_init', array( $this, 'admin_init' ) );
686
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
687
688
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
689
690
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
691
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
692
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
693
694
		// returns HTTPS support status
695
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
696
697
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
698
699
		add_action( 'wp_ajax_jetpack_wizard_banner', array( 'Jetpack_Wizard_Banner', 'ajax_callback' ) );
700
701
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
702
703
		/**
704
		 * These actions run checks to load additional files.
705
		 * They check for external files or plugins, so they need to run as late as possible.
706
		 */
707
		add_action( 'wp_head', array( $this, 'check_open_graph' ), 1 );
708
		add_action( 'amp_story_head', array( $this, 'check_open_graph' ), 1 );
709
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ), 999 );
710
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
711
712
		add_filter( 'plugins_url', array( 'Jetpack', 'maybe_min_asset' ), 1, 3 );
713
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2 );
714
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
715
716
		add_filter( 'profile_update', array( 'Jetpack', 'user_meta_cleanup' ) );
717
718
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
719
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
720
721
		// A filter to control all just in time messages
722
		add_filter( 'jetpack_just_in_time_msgs', '__return_true', 9 );
723
724
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9 );
725
726
		/*
727
		 * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
728
		 * We should make sure to only do this for front end links.
729
		 */
730
		if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
731
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
732
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
733
734
			/*
735
			 * We'll override wp_notify_postauthor and wp_notify_moderator pluggable functions
736
			 * so they point moderation links on emails to Calypso.
737
			 */
738
			jetpack_require_lib( 'functions.wp-notify' );
739
		}
740
741
		add_action(
742
			'plugins_loaded',
743
			function() {
744
				if ( User_Agent_Info::is_mobile_app() ) {
745
					add_filter( 'get_edit_post_link', '__return_empty_string' );
746
				}
747
			}
748
		);
749
750
		// Update the site's Jetpack plan and products from API on heartbeats.
751
		add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
752
753
		/**
754
		 * This is the hack to concatenate all css files into one.
755
		 * For description and reasoning see the implode_frontend_css method.
756
		 *
757
		 * Super late priority so we catch all the registered styles.
758
		 */
759
		if ( ! is_admin() ) {
760
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
761
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
762
		}
763
764
		/**
765
		 * These are sync actions that we need to keep track of for jitms
766
		 */
767
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
768
769
		// Actually push the stats on shutdown.
770
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
771
			add_action( 'shutdown', array( $this, 'push_stats' ) );
772
		}
773
774
		// Actions for Manager::authorize().
775
		add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
776
		add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
777
		add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
778
779
		// Filters for the Manager::get_token() urls and request body.
780
		add_filter( 'jetpack_token_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
781
		add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
782
		add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
783
784
		// Actions for successful reconnect.
785
		add_action( 'jetpack_reconnection_completed', array( $this, 'reconnection_completed' ) );
786
	}
787
788
	/**
789
	 * Before everything else starts getting initalized, we need to initialize Jetpack using the
790
	 * Config object.
791
	 */
792
	public function configure() {
793
		$config = new Config();
794
795
		foreach (
796
			array(
797
				'sync',
798
			)
799
			as $feature
800
		) {
801
			$config->ensure( $feature );
802
		}
803
804
		$config->ensure(
805
			'connection',
806
			array(
807
				'slug' => 'jetpack',
808
				'name' => 'Jetpack',
809
			)
810
		);
811
812
		if ( is_admin() ) {
813
			$config->ensure( 'jitm' );
814
		}
815
816
		if ( ! $this->connection_manager ) {
817
			$this->connection_manager = new Connection_Manager( 'jetpack' );
818
		}
819
820
		/*
821
		 * Load things that should only be in Network Admin.
822
		 *
823
		 * For now blow away everything else until a more full
824
		 * understanding of what is needed at the network level is
825
		 * available
826
		 */
827
		if ( is_multisite() ) {
828
			$network = Jetpack_Network::init();
829
			$network->set_connection( $this->connection_manager );
830
		}
831
832
		if ( $this->connection_manager->is_active() ) {
833
			add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
834
835
			Jetpack_Heartbeat::init();
836
			if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
837
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
838
				Jetpack_Search_Performance_Logger::init();
839
			}
840
		}
841
842
		// Initialize remote file upload request handlers.
843
		$this->add_remote_request_handlers();
844
845
		/*
846
		 * Enable enhanced handling of previewing sites in Calypso
847
		 */
848
		if ( self::is_active() ) {
849
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
850
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
851
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
852
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
853
		}
854
855
		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...
856
			add_action( 'init', array( new Plugin_Tracking(), 'init' ) );
857
		} else {
858
			/**
859
			 * Initialize tracking right after the user agrees to the terms of service.
860
			 */
861
			add_action( 'jetpack_agreed_to_terms_of_service', array( new Plugin_Tracking(), 'init' ) );
862
		}
863
	}
864
865
	/**
866
	 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
867
	 * initialization code.
868
	 *
869
	 * @action plugins_loaded
870
	 */
871
	public function late_initialization() {
872
		add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
873
874
		Partner::init();
875
876
		/**
877
		 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
878
		 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
879
		 *
880
		 * @since 8.1.0
881
		 *
882
		 * @param Jetpack $jetpack the main plugin class object.
883
		 */
884
		do_action( 'jetpack_loaded', $this );
885
886
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
887
	}
888
889
	/**
890
	 * Sets up the XMLRPC request handlers.
891
	 *
892
	 * @deprecated since 7.7.0
893
	 * @see Automattic\Jetpack\Connection\Manager::setup_xmlrpc_handlers()
894
	 *
895
	 * @param array                 $request_params Incoming request parameters.
896
	 * @param Boolean               $is_active      Whether the connection is currently active.
897
	 * @param Boolean               $is_signed      Whether the signature check has been successful.
898
	 * @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...
899
	 */
900 View Code Duplication
	public function setup_xmlrpc_handlers(
901
		$request_params,
902
		$is_active,
903
		$is_signed,
904
		Jetpack_XMLRPC_Server $xmlrpc_server = null
905
	) {
906
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::setup_xmlrpc_handlers' );
907
908
		if ( ! $this->connection_manager ) {
909
			$this->connection_manager = new Connection_Manager();
910
		}
911
912
		return $this->connection_manager->setup_xmlrpc_handlers(
913
			$request_params,
914
			$is_active,
915
			$is_signed,
916
			$xmlrpc_server
917
		);
918
	}
919
920
	/**
921
	 * Initialize REST API registration connector.
922
	 *
923
	 * @deprecated since 7.7.0
924
	 * @see Automattic\Jetpack\Connection\Manager::initialize_rest_api_registration_connector()
925
	 */
926 View Code Duplication
	public function initialize_rest_api_registration_connector() {
927
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::initialize_rest_api_registration_connector' );
928
929
		if ( ! $this->connection_manager ) {
930
			$this->connection_manager = new Connection_Manager();
931
		}
932
933
		$this->connection_manager->initialize_rest_api_registration_connector();
934
	}
935
936
	/**
937
	 * This is ported over from the manage module, which has been deprecated and baked in here.
938
	 *
939
	 * @param $domains
940
	 */
941
	function add_wpcom_to_allowed_redirect_hosts( $domains ) {
942
		add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) );
943
	}
944
945
	/**
946
	 * Return $domains, with 'wordpress.com' appended.
947
	 * This is ported over from the manage module, which has been deprecated and baked in here.
948
	 *
949
	 * @param $domains
950
	 * @return array
951
	 */
952
	function allow_wpcom_domain( $domains ) {
953
		if ( empty( $domains ) ) {
954
			$domains = array();
955
		}
956
		$domains[] = 'wordpress.com';
957
		return array_unique( $domains );
958
	}
959
960
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
961
		$post = get_post( $post_id );
962
963
		if ( empty( $post ) ) {
964
			return $default_url;
965
		}
966
967
		$post_type = $post->post_type;
968
969
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
970
		// https://en.support.wordpress.com/custom-post-types/
971
		$allowed_post_types = array(
972
			'post',
973
			'page',
974
			'jetpack-portfolio',
975
			'jetpack-testimonial',
976
		);
977
978
		if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
979
			return $default_url;
980
		}
981
982
		return Redirect::get_url(
983
			'calypso-edit-' . $post_type,
984
			array(
985
				'path' => $post_id,
986
			)
987
		);
988
	}
989
990
	function point_edit_comment_links_to_calypso( $url ) {
991
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
992
		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...
The call to wp_parse_url() has too many arguments starting with PHP_URL_QUERY.

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...
993
994
		return Redirect::get_url(
995
			'calypso-edit-comment',
996
			array(
997
				'path' => $query_args['amp;c'],
998
			)
999
		);
1000
1001
	}
1002
1003
	function jetpack_track_last_sync_callback( $params ) {
1004
		/**
1005
		 * Filter to turn off jitm caching
1006
		 *
1007
		 * @since 5.4.0
1008
		 *
1009
		 * @param bool false Whether to cache just in time messages
1010
		 */
1011
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
1012
			return $params;
1013
		}
1014
1015
		if ( is_array( $params ) && isset( $params[0] ) ) {
1016
			$option = $params[0];
1017
			if ( 'active_plugins' === $option ) {
1018
				// use the cache if we can, but not terribly important if it gets evicted
1019
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
1020
			}
1021
		}
1022
1023
		return $params;
1024
	}
1025
1026
	function jetpack_connection_banner_callback() {
1027
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
1028
1029
		// Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
1030
		if (
1031
			isset( $_REQUEST['dismissBanner'] ) &&
1032
			! Jetpack_Connection_Banner::force_display()
1033
		) {
1034
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1035
			wp_send_json_success();
1036
		}
1037
1038
		wp_die();
1039
	}
1040
1041
	/**
1042
	 * Removes all XML-RPC methods that are not `jetpack.*`.
1043
	 * Only used in our alternate XML-RPC endpoint, where we want to
1044
	 * ensure that Core and other plugins' methods are not exposed.
1045
	 *
1046
	 * @deprecated since 7.7.0
1047
	 * @see Automattic\Jetpack\Connection\Manager::remove_non_jetpack_xmlrpc_methods()
1048
	 *
1049
	 * @param array $methods A list of registered WordPress XMLRPC methods.
1050
	 * @return array Filtered $methods
1051
	 */
1052 View Code Duplication
	public function remove_non_jetpack_xmlrpc_methods( $methods ) {
1053
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::remove_non_jetpack_xmlrpc_methods' );
1054
1055
		if ( ! $this->connection_manager ) {
1056
			$this->connection_manager = new Connection_Manager();
1057
		}
1058
1059
		return $this->connection_manager->remove_non_jetpack_xmlrpc_methods( $methods );
1060
	}
1061
1062
	/**
1063
	 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
1064
	 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
1065
	 * security/firewall policies, we provide our own alternate XML RPC API endpoint
1066
	 * which is accessible via a different URI. Most of the below is copied directly
1067
	 * from /xmlrpc.php so that we're replicating it as closely as possible.
1068
	 *
1069
	 * @deprecated since 7.7.0
1070
	 * @see Automattic\Jetpack\Connection\Manager::alternate_xmlrpc()
1071
	 */
1072 View Code Duplication
	public function alternate_xmlrpc() {
1073
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::alternate_xmlrpc' );
1074
1075
		if ( ! $this->connection_manager ) {
1076
			$this->connection_manager = new Connection_Manager();
1077
		}
1078
1079
		$this->connection_manager->alternate_xmlrpc();
1080
	}
1081
1082
	/**
1083
	 * The callback for the JITM ajax requests.
1084
	 *
1085
	 * @deprecated since 7.9.0
1086
	 */
1087
	function jetpack_jitm_ajax_callback() {
1088
		_deprecated_function( __METHOD__, 'jetpack-7.9' );
1089
	}
1090
1091
	/**
1092
	 * If there are any stats that need to be pushed, but haven't been, push them now.
1093
	 */
1094
	function push_stats() {
1095
		if ( ! empty( $this->stats ) ) {
1096
			$this->do_stats( 'server_side' );
1097
		}
1098
	}
1099
1100
	/**
1101
	 * Sets the Jetpack custom capabilities.
1102
	 *
1103
	 * @param string[] $caps    Array of the user's capabilities.
1104
	 * @param string   $cap     Capability name.
1105
	 * @param int      $user_id The user ID.
1106
	 * @param array    $args    Adds the context to the cap. Typically the object ID.
1107
	 */
1108
	public function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
1109
		$is_offline_mode = ( new Status() )->is_offline_mode();
1110
		switch ( $cap ) {
1111
			case 'jetpack_manage_modules':
1112
			case 'jetpack_activate_modules':
1113
			case 'jetpack_deactivate_modules':
1114
				$caps = array( 'manage_options' );
1115
				break;
1116
			case 'jetpack_configure_modules':
1117
				$caps = array( 'manage_options' );
1118
				break;
1119
			case 'jetpack_manage_autoupdates':
1120
				$caps = array(
1121
					'manage_options',
1122
					'update_plugins',
1123
				);
1124
				break;
1125
			case 'jetpack_network_admin_page':
1126
			case 'jetpack_network_settings_page':
1127
				$caps = array( 'manage_network_plugins' );
1128
				break;
1129
			case 'jetpack_network_sites_page':
1130
				$caps = array( 'manage_sites' );
1131
				break;
1132
			case 'jetpack_admin_page':
1133
				if ( $is_offline_mode ) {
1134
					$caps = array( 'manage_options' );
1135
					break;
1136
				} else {
1137
					$caps = array( 'read' );
1138
				}
1139
				break;
1140
		}
1141
		return $caps;
1142
	}
1143
1144
	/**
1145
	 * Require a Jetpack authentication.
1146
	 *
1147
	 * @deprecated since 7.7.0
1148
	 * @see Automattic\Jetpack\Connection\Manager::require_jetpack_authentication()
1149
	 */
1150 View Code Duplication
	public function require_jetpack_authentication() {
1151
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::require_jetpack_authentication' );
1152
1153
		if ( ! $this->connection_manager ) {
1154
			$this->connection_manager = new Connection_Manager();
1155
		}
1156
1157
		$this->connection_manager->require_jetpack_authentication();
1158
	}
1159
1160
	/**
1161
	 * Register assets for use in various modules and the Jetpack admin page.
1162
	 *
1163
	 * @uses wp_script_is, wp_register_script, plugins_url
1164
	 * @action wp_loaded
1165
	 * @return null
1166
	 */
1167
	public function register_assets() {
1168 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1169
			wp_register_script(
1170
				'jetpack-gallery-settings',
1171
				Assets::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1172
				array( 'media-views' ),
1173
				'20121225'
1174
			);
1175
		}
1176
1177
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1178
			wp_register_script(
1179
				'jetpack-twitter-timeline',
1180
				Assets::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1181
				array( 'jquery' ),
1182
				'4.0.0',
1183
				true
1184
			);
1185
		}
1186
1187
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1188
			wp_register_script(
1189
				'jetpack-facebook-embed',
1190
				Assets::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1191
				array(),
1192
				null,
1193
				true
1194
			);
1195
1196
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1197
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1198
			if ( ! is_numeric( $fb_app_id ) ) {
1199
				$fb_app_id = '';
1200
			}
1201
			wp_localize_script(
1202
				'jetpack-facebook-embed',
1203
				'jpfbembed',
1204
				array(
1205
					'appid'  => $fb_app_id,
1206
					'locale' => $this->get_locale(),
1207
				)
1208
			);
1209
		}
1210
1211
		/**
1212
		 * As jetpack_register_genericons is by default fired off a hook,
1213
		 * the hook may have already fired by this point.
1214
		 * So, let's just trigger it manually.
1215
		 */
1216
		require_once JETPACK__PLUGIN_DIR . '_inc/genericons.php';
1217
		jetpack_register_genericons();
1218
1219
		/**
1220
		 * Register the social logos
1221
		 */
1222
		require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1223
		jetpack_register_social_logos();
1224
1225 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1226
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1227
		}
1228
	}
1229
1230
	/**
1231
	 * Guess locale from language code.
1232
	 *
1233
	 * @param string $lang Language code.
1234
	 * @return string|bool
1235
	 */
1236 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1237
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1238
			return 'en_US';
1239
		}
1240
1241
		if ( ! class_exists( 'GP_Locales' ) ) {
1242
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1243
				return false;
1244
			}
1245
1246
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1247
		}
1248
1249
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1250
			// WP.com: get_locale() returns 'it'
1251
			$locale = GP_Locales::by_slug( $lang );
1252
		} else {
1253
			// Jetpack: get_locale() returns 'it_IT';
1254
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1255
		}
1256
1257
		if ( ! $locale ) {
1258
			return false;
1259
		}
1260
1261
		if ( empty( $locale->facebook_locale ) ) {
1262
			if ( empty( $locale->wp_locale ) ) {
1263
				return false;
1264
			} else {
1265
				// Facebook SDK is smart enough to fall back to en_US if a
1266
				// locale isn't supported. Since supported Facebook locales
1267
				// can fall out of sync, we'll attempt to use the known
1268
				// wp_locale value and rely on said fallback.
1269
				return $locale->wp_locale;
1270
			}
1271
		}
1272
1273
		return $locale->facebook_locale;
1274
	}
1275
1276
	/**
1277
	 * Get the locale.
1278
	 *
1279
	 * @return string|bool
1280
	 */
1281
	function get_locale() {
1282
		$locale = $this->guess_locale_from_lang( get_locale() );
1283
1284
		if ( ! $locale ) {
1285
			$locale = 'en_US';
1286
		}
1287
1288
		return $locale;
1289
	}
1290
1291
	/**
1292
	 * Return the network_site_url so that .com knows what network this site is a part of.
1293
	 *
1294
	 * @param  bool $option
1295
	 * @return string
1296
	 */
1297
	public function jetpack_main_network_site_option( $option ) {
1298
		return network_site_url();
1299
	}
1300
	/**
1301
	 * Network Name.
1302
	 */
1303
	static function network_name( $option = null ) {
1304
		global $current_site;
1305
		return $current_site->site_name;
1306
	}
1307
	/**
1308
	 * Does the network allow new user and site registrations.
1309
	 *
1310
	 * @return string
1311
	 */
1312
	static function network_allow_new_registrations( $option = null ) {
1313
		return ( in_array( get_site_option( 'registration' ), array( 'none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration' ) : 'none' );
1314
	}
1315
	/**
1316
	 * Does the network allow admins to add new users.
1317
	 *
1318
	 * @return boolian
1319
	 */
1320
	static function network_add_new_users( $option = null ) {
1321
		return (bool) get_site_option( 'add_new_users' );
1322
	}
1323
	/**
1324
	 * File upload psace left per site in MB.
1325
	 *  -1 means NO LIMIT.
1326
	 *
1327
	 * @return number
1328
	 */
1329
	static function network_site_upload_space( $option = null ) {
1330
		// value in MB
1331
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1332
	}
1333
1334
	/**
1335
	 * Network allowed file types.
1336
	 *
1337
	 * @return string
1338
	 */
1339
	static function network_upload_file_types( $option = null ) {
1340
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1341
	}
1342
1343
	/**
1344
	 * Maximum file upload size set by the network.
1345
	 *
1346
	 * @return number
1347
	 */
1348
	static function network_max_upload_file_size( $option = null ) {
1349
		// value in KB
1350
		return get_site_option( 'fileupload_maxk', 300 );
1351
	}
1352
1353
	/**
1354
	 * Lets us know if a site allows admins to manage the network.
1355
	 *
1356
	 * @return array
1357
	 */
1358
	static function network_enable_administration_menus( $option = null ) {
1359
		return get_site_option( 'menu_items' );
1360
	}
1361
1362
	/**
1363
	 * If a user has been promoted to or demoted from admin, we need to clear the
1364
	 * jetpack_other_linked_admins transient.
1365
	 *
1366
	 * @since 4.3.2
1367
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1368
	 *
1369
	 * @param int    $user_id   The user ID whose role changed.
1370
	 * @param string $role      The new role.
1371
	 * @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...
1372
	 */
1373
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1374
		if ( 'administrator' == $role
1375
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1376
			|| is_null( $old_roles )
1377
		) {
1378
			delete_transient( 'jetpack_other_linked_admins' );
1379
		}
1380
	}
1381
1382
	/**
1383
	 * Checks to see if there are any other users available to become primary
1384
	 * Users must both:
1385
	 * - Be linked to wpcom
1386
	 * - Be an admin
1387
	 *
1388
	 * @return mixed False if no other users are linked, Int if there are.
1389
	 */
1390
	static function get_other_linked_admins() {
1391
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1392
1393
		if ( false === $other_linked_users ) {
1394
			$admins = get_users( array( 'role' => 'administrator' ) );
1395
			if ( count( $admins ) > 1 ) {
1396
				$available = array();
1397
				foreach ( $admins as $admin ) {
1398
					if ( self::is_user_connected( $admin->ID ) ) {
1399
						$available[] = $admin->ID;
1400
					}
1401
				}
1402
1403
				$count_connected_admins = count( $available );
1404
				if ( count( $available ) > 1 ) {
1405
					$other_linked_users = $count_connected_admins;
1406
				} else {
1407
					$other_linked_users = 0;
1408
				}
1409
			} else {
1410
				$other_linked_users = 0;
1411
			}
1412
1413
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1414
		}
1415
1416
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1417
	}
1418
1419
	/**
1420
	 * Return whether we are dealing with a multi network setup or not.
1421
	 * The reason we are type casting this is because we want to avoid the situation where
1422
	 * the result is false since when is_main_network_option return false it cases
1423
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1424
	 * database which could be set to anything as opposed to what this function returns.
1425
	 *
1426
	 * @param  bool $option
1427
	 *
1428
	 * @return boolean
1429
	 */
1430
	public function is_main_network_option( $option ) {
1431
		// return '1' or ''
1432
		return (string) (bool) self::is_multi_network();
1433
	}
1434
1435
	/**
1436
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1437
	 *
1438
	 * @param  string $option
1439
	 * @return boolean
1440
	 */
1441
	public function is_multisite( $option ) {
1442
		return (string) (bool) is_multisite();
1443
	}
1444
1445
	/**
1446
	 * Implemented since there is no core is multi network function
1447
	 * Right now there is no way to tell if we which network is the dominant network on the system
1448
	 *
1449
	 * @since  3.3
1450
	 * @return boolean
1451
	 */
1452 View Code Duplication
	public static function is_multi_network() {
1453
		global  $wpdb;
1454
1455
		// if we don't have a multi site setup no need to do any more
1456
		if ( ! is_multisite() ) {
1457
			return false;
1458
		}
1459
1460
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1461
		if ( $num_sites > 1 ) {
1462
			return true;
1463
		} else {
1464
			return false;
1465
		}
1466
	}
1467
1468
	/**
1469
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1470
	 *
1471
	 * @return null
1472
	 */
1473
	function update_jetpack_main_network_site_option() {
1474
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1475
	}
1476
	/**
1477
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1478
	 */
1479
	function update_jetpack_network_settings() {
1480
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1481
		// Only sync this info for the main network site.
1482
	}
1483
1484
	/**
1485
	 * Get back if the current site is single user site.
1486
	 *
1487
	 * @return bool
1488
	 */
1489 View Code Duplication
	public static function is_single_user_site() {
1490
		global $wpdb;
1491
1492
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1493
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1494
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1495
		}
1496
		return 1 === (int) $some_users;
1497
	}
1498
1499
	/**
1500
	 * Returns true if the site has file write access false otherwise.
1501
	 *
1502
	 * @return string ( '1' | '0' )
1503
	 **/
1504
	public static function file_system_write_access() {
1505
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1506
			require_once ABSPATH . 'wp-admin/includes/file.php';
1507
		}
1508
1509
		require_once ABSPATH . 'wp-admin/includes/template.php';
1510
1511
		$filesystem_method = get_filesystem_method();
1512
		if ( $filesystem_method === 'direct' ) {
1513
			return 1;
1514
		}
1515
1516
		ob_start();
1517
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1518
		ob_end_clean();
1519
		if ( $filesystem_credentials_are_stored ) {
1520
			return 1;
1521
		}
1522
		return 0;
1523
	}
1524
1525
	/**
1526
	 * Finds out if a site is using a version control system.
1527
	 *
1528
	 * @return string ( '1' | '0' )
1529
	 **/
1530
	public static function is_version_controlled() {
1531
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' );
1532
		return (string) (int) Functions::is_version_controlled();
1533
	}
1534
1535
	/**
1536
	 * Determines whether the current theme supports featured images or not.
1537
	 *
1538
	 * @return string ( '1' | '0' )
1539
	 */
1540
	public static function featured_images_enabled() {
1541
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1542
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1543
	}
1544
1545
	/**
1546
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1547
	 *
1548
	 * @deprecated 4.7 use get_avatar_url instead.
1549
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1550
	 * @param int               $size Size of the avatar image
1551
	 * @param string            $default URL to a default image to use if no avatar is available
1552
	 * @param bool              $force_display Whether to force it to return an avatar even if show_avatars is disabled
1553
	 *
1554
	 * @return array
1555
	 */
1556
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1557
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1558
		return get_avatar_url(
1559
			$id_or_email,
1560
			array(
1561
				'size'          => $size,
1562
				'default'       => $default,
1563
				'force_default' => $force_display,
1564
			)
1565
		);
1566
	}
1567
1568
	/**
1569
	 * jetpack_updates is saved in the following schema:
1570
	 *
1571
	 * array (
1572
	 *      'plugins'                       => (int) Number of plugin updates available.
1573
	 *      'themes'                        => (int) Number of theme updates available.
1574
	 *      'wordpress'                     => (int) Number of WordPress core updates available. // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
1575
	 *      'translations'                  => (int) Number of translation updates available.
1576
	 *      'total'                         => (int) Total of all available updates.
1577
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1578
	 * )
1579
	 *
1580
	 * @return array
1581
	 */
1582
	public static function get_updates() {
1583
		$update_data = wp_get_update_data();
1584
1585
		// Stores the individual update counts as well as the total count.
1586
		if ( isset( $update_data['counts'] ) ) {
1587
			$updates = $update_data['counts'];
1588
		}
1589
1590
		// If we need to update WordPress core, let's find the latest version number.
1591 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1592
			$cur = get_preferred_from_update_core();
1593
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1594
				$updates['wp_update_version'] = $cur->current;
1595
			}
1596
		}
1597
		return isset( $updates ) ? $updates : array();
1598
	}
1599
1600
	public static function get_update_details() {
1601
		$update_details = array(
1602
			'update_core'    => get_site_transient( 'update_core' ),
1603
			'update_plugins' => get_site_transient( 'update_plugins' ),
1604
			'update_themes'  => get_site_transient( 'update_themes' ),
1605
		);
1606
		return $update_details;
1607
	}
1608
1609
	public static function refresh_update_data() {
1610
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1611
1612
	}
1613
1614
	public static function refresh_theme_data() {
1615
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1616
	}
1617
1618
	/**
1619
	 * Is Jetpack active?
1620
	 * The method only checks if there's an existing token for the master user. It doesn't validate the token.
1621
	 *
1622
	 * @return bool
1623
	 */
1624
	public static function is_active() {
1625
		return self::connection()->is_active();
1626
	}
1627
1628
	/**
1629
	 * Make an API call to WordPress.com for plan status
1630
	 *
1631
	 * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom.
1632
	 *
1633
	 * @return bool True if plan is updated, false if no update
1634
	 */
1635
	public static function refresh_active_plan_from_wpcom() {
1636
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' );
1637
		return Jetpack_Plan::refresh_from_wpcom();
1638
	}
1639
1640
	/**
1641
	 * Get the plan that this Jetpack site is currently using
1642
	 *
1643
	 * @deprecated 7.2.0 Use Jetpack_Plan::get.
1644
	 * @return array Active Jetpack plan details.
1645
	 */
1646
	public static function get_active_plan() {
1647
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' );
1648
		return Jetpack_Plan::get();
1649
	}
1650
1651
	/**
1652
	 * Determine whether the active plan supports a particular feature
1653
	 *
1654
	 * @deprecated 7.2.0 Use Jetpack_Plan::supports.
1655
	 * @return bool True if plan supports feature, false if not.
1656
	 */
1657
	public static function active_plan_supports( $feature ) {
1658
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' );
1659
		return Jetpack_Plan::supports( $feature );
1660
	}
1661
1662
	/**
1663
	 * Deprecated: Is Jetpack in development (offline) mode?
1664
	 *
1665
	 * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1666
	 * and themes still use it, and we do not want to flood them with notices.
1667
	 *
1668
	 * Please use Automattic\Jetpack\Status()->is_offline_mode() instead.
1669
	 *
1670
	 * @deprecated since 8.0.
1671
	 */
1672
	public static function is_development_mode() {
1673
		return ( new Status() )->is_offline_mode();
1674
	}
1675
1676
	/**
1677
	 * Whether the site is currently onboarding or not.
1678
	 * A site is considered as being onboarded if it currently has an onboarding token.
1679
	 *
1680
	 * @since 5.8
1681
	 *
1682
	 * @access public
1683
	 * @static
1684
	 *
1685
	 * @return bool True if the site is currently onboarding, false otherwise
1686
	 */
1687
	public static function is_onboarding() {
1688
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1689
	}
1690
1691
	/**
1692
	 * Determines reason for Jetpack offline mode.
1693
	 */
1694
	public static function development_mode_trigger_text() {
1695
		$status = new Status();
1696
1697
		if ( ! $status->is_offline_mode() ) {
1698
			return __( 'Jetpack is not in Offline Mode.', 'jetpack' );
1699
		}
1700
1701
		if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1702
			$notice = __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1703
		} elseif ( defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV ) {
1704
			$notice = __( 'The WP_LOCAL_DEV constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1705
		} elseif ( $status->is_local_site() ) {
1706
			$notice = __( 'The site URL is a known local development environment URL (e.g. http://localhost).', 'jetpack' );
1707
			/** This filter is documented in packages/status/src/class-status.php */
1708
		} elseif ( has_filter( 'jetpack_development_mode' ) && apply_filters( 'jetpack_development_mode', false ) ) { // This is a deprecated filter name.
1709
			$notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1710
		} else {
1711
			$notice = __( 'The jetpack_offline_mode filter is set to true.', 'jetpack' );
1712
		}
1713
1714
		return $notice;
1715
1716
	}
1717
	/**
1718
	 * Get Jetpack offline mode notice text and notice class.
1719
	 *
1720
	 * Mirrors the checks made in Automattic\Jetpack\Status->is_offline_mode
1721
	 */
1722
	public static function show_development_mode_notice() {
1723 View Code Duplication
		if ( ( new Status() )->is_offline_mode() ) {
1724
			$notice = sprintf(
1725
				/* translators: %s is a URL */
1726
				__( 'In <a href="%s" target="_blank">Offline Mode</a>:', 'jetpack' ),
1727
				Redirect::get_url( 'jetpack-support-development-mode' )
1728
			);
1729
1730
			$notice .= ' ' . self::development_mode_trigger_text();
1731
1732
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1733
		}
1734
1735
		// Throw up a notice if using a development version and as for feedback.
1736
		if ( self::is_development_version() ) {
1737
			/* translators: %s is a URL */
1738
			$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' ) );
1739
1740
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1741
		}
1742
		// Throw up a notice if using staging mode
1743 View Code Duplication
		if ( ( new Status() )->is_staging_site() ) {
1744
			/* translators: %s is a URL */
1745
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1746
1747
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1748
		}
1749
	}
1750
1751
	/**
1752
	 * Whether Jetpack's version maps to a public release, or a development version.
1753
	 */
1754
	public static function is_development_version() {
1755
		/**
1756
		 * Allows filtering whether this is a development version of Jetpack.
1757
		 *
1758
		 * This filter is especially useful for tests.
1759
		 *
1760
		 * @since 4.3.0
1761
		 *
1762
		 * @param bool $development_version Is this a develoment version of Jetpack?
1763
		 */
1764
		return (bool) apply_filters(
1765
			'jetpack_development_version',
1766
			! preg_match( '/^\d+(\.\d+)+$/', Constants::get_constant( 'JETPACK__VERSION' ) )
1767
		);
1768
	}
1769
1770
	/**
1771
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1772
	 */
1773
	public static function is_user_connected( $user_id = false ) {
1774
		return self::connection()->is_user_connected( $user_id );
1775
	}
1776
1777
	/**
1778
	 * Get the wpcom user data of the current|specified connected user.
1779
	 */
1780 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1781
		// TODO: remove in favor of Connection_Manager->get_connected_user_data
1782
		if ( ! $user_id ) {
1783
			$user_id = get_current_user_id();
1784
		}
1785
1786
		$transient_key = "jetpack_connected_user_data_$user_id";
1787
1788
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1789
			return $cached_user_data;
1790
		}
1791
1792
		$xml = new Jetpack_IXR_Client(
1793
			array(
1794
				'user_id' => $user_id,
1795
			)
1796
		);
1797
		$xml->query( 'wpcom.getUser' );
1798
		if ( ! $xml->isError() ) {
1799
			$user_data = $xml->getResponse();
1800
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1801
			return $user_data;
1802
		}
1803
1804
		return false;
1805
	}
1806
1807
	/**
1808
	 * Get the wpcom email of the current|specified connected user.
1809
	 */
1810
	public static function get_connected_user_email( $user_id = null ) {
1811
		if ( ! $user_id ) {
1812
			$user_id = get_current_user_id();
1813
		}
1814
1815
		$xml = new Jetpack_IXR_Client(
1816
			array(
1817
				'user_id' => $user_id,
1818
			)
1819
		);
1820
		$xml->query( 'wpcom.getUserEmail' );
1821
		if ( ! $xml->isError() ) {
1822
			return $xml->getResponse();
1823
		}
1824
		return false;
1825
	}
1826
1827
	/**
1828
	 * Get the wpcom email of the master user.
1829
	 */
1830
	public static function get_master_user_email() {
1831
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1832
		if ( $master_user_id ) {
1833
			return self::get_connected_user_email( $master_user_id );
1834
		}
1835
		return '';
1836
	}
1837
1838
	/**
1839
	 * Whether the current user is the connection owner.
1840
	 *
1841
	 * @deprecated since 7.7
1842
	 *
1843
	 * @return bool Whether the current user is the connection owner.
1844
	 */
1845
	public function current_user_is_connection_owner() {
1846
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::is_connection_owner' );
1847
		return self::connection()->is_connection_owner();
1848
	}
1849
1850
	/**
1851
	 * Gets current user IP address.
1852
	 *
1853
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1854
	 *
1855
	 * @return string                  Current user IP address.
1856
	 */
1857
	public static function current_user_ip( $check_all_headers = false ) {
1858
		if ( $check_all_headers ) {
1859
			foreach ( array(
1860
				'HTTP_CF_CONNECTING_IP',
1861
				'HTTP_CLIENT_IP',
1862
				'HTTP_X_FORWARDED_FOR',
1863
				'HTTP_X_FORWARDED',
1864
				'HTTP_X_CLUSTER_CLIENT_IP',
1865
				'HTTP_FORWARDED_FOR',
1866
				'HTTP_FORWARDED',
1867
				'HTTP_VIA',
1868
			) as $key ) {
1869
				if ( ! empty( $_SERVER[ $key ] ) ) {
1870
					return $_SERVER[ $key ];
1871
				}
1872
			}
1873
		}
1874
1875
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1876
	}
1877
1878
	/**
1879
	 * Synchronize connected user role changes
1880
	 */
1881
	function user_role_change( $user_id ) {
1882
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' );
1883
		Users::user_role_change( $user_id );
1884
	}
1885
1886
	/**
1887
	 * Loads the currently active modules.
1888
	 */
1889
	public static function load_modules() {
1890
		$is_offline_mode = ( new Status() )->is_offline_mode();
1891
		if (
1892
			! self::is_active()
1893
			&& ! $is_offline_mode
1894
			&& ! self::is_onboarding()
1895
			&& (
1896
				! is_multisite()
1897
				|| ! get_site_option( 'jetpack_protect_active' )
1898
			)
1899
		) {
1900
			return;
1901
		}
1902
1903
		$version = Jetpack_Options::get_option( 'version' );
1904 View Code Duplication
		if ( ! $version ) {
1905
			$version = $old_version = JETPACK__VERSION . ':' . time();
1906
			/** This action is documented in class.jetpack.php */
1907
			do_action( 'updating_jetpack_version', $version, false );
1908
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1909
		}
1910
		list( $version ) = explode( ':', $version );
1911
1912
		$modules = array_filter( self::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1913
1914
		$modules_data = array();
1915
1916
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1917
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1918
			$updated_modules = array();
1919
			foreach ( $modules as $module ) {
1920
				$modules_data[ $module ] = self::get_module( $module );
1921
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1922
					continue;
1923
				}
1924
1925
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1926
					continue;
1927
				}
1928
1929
				$updated_modules[] = $module;
1930
			}
1931
1932
			$modules = array_diff( $modules, $updated_modules );
1933
		}
1934
1935
		foreach ( $modules as $index => $module ) {
1936
			// If we're in offline mode, disable modules requiring a connection.
1937
			if ( $is_offline_mode ) {
1938
				// Prime the pump if we need to
1939
				if ( empty( $modules_data[ $module ] ) ) {
1940
					$modules_data[ $module ] = self::get_module( $module );
1941
				}
1942
				// If the module requires a connection, but we're in local mode, don't include it.
1943
				if ( $modules_data[ $module ]['requires_connection'] ) {
1944
					continue;
1945
				}
1946
			}
1947
1948
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1949
				continue;
1950
			}
1951
1952
			if ( ! include_once self::get_module_path( $module ) ) {
1953
				unset( $modules[ $index ] );
1954
				self::update_active_modules( array_values( $modules ) );
1955
				continue;
1956
			}
1957
1958
			/**
1959
			 * Fires when a specific module is loaded.
1960
			 * The dynamic part of the hook, $module, is the module slug.
1961
			 *
1962
			 * @since 1.1.0
1963
			 */
1964
			do_action( 'jetpack_module_loaded_' . $module );
1965
		}
1966
1967
		/**
1968
		 * Fires when all the modules are loaded.
1969
		 *
1970
		 * @since 1.1.0
1971
		 */
1972
		do_action( 'jetpack_modules_loaded' );
1973
1974
		// 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.
1975
		require_once JETPACK__PLUGIN_DIR . 'modules/module-extras.php';
1976
	}
1977
1978
	/**
1979
	 * Check if Jetpack's REST API compat file should be included
1980
	 *
1981
	 * @action plugins_loaded
1982
	 * @return null
1983
	 */
1984
	public function check_rest_api_compat() {
1985
		/**
1986
		 * Filters the list of REST API compat files to be included.
1987
		 *
1988
		 * @since 2.2.5
1989
		 *
1990
		 * @param array $args Array of REST API compat files to include.
1991
		 */
1992
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1993
1994
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include ) {
1995
			require_once $_jetpack_rest_api_compat_include;
1996
		}
1997
	}
1998
1999
	/**
2000
	 * Gets all plugins currently active in values, regardless of whether they're
2001
	 * traditionally activated or network activated.
2002
	 *
2003
	 * @todo Store the result in core's object cache maybe?
2004
	 */
2005
	public static function get_active_plugins() {
2006
		$active_plugins = (array) get_option( 'active_plugins', array() );
2007
2008
		if ( is_multisite() ) {
2009
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
2010
			// whereas active_plugins stores them in the values.
2011
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
2012
			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...
2013
				$active_plugins = array_merge( $active_plugins, $network_plugins );
2014
			}
2015
		}
2016
2017
		sort( $active_plugins );
2018
2019
		return array_unique( $active_plugins );
2020
	}
2021
2022
	/**
2023
	 * Gets and parses additional plugin data to send with the heartbeat data
2024
	 *
2025
	 * @since 3.8.1
2026
	 *
2027
	 * @return array Array of plugin data
2028
	 */
2029
	public static function get_parsed_plugin_data() {
2030
		if ( ! function_exists( 'get_plugins' ) ) {
2031
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
2032
		}
2033
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
2034
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
2035
		$active_plugins = self::get_active_plugins();
2036
2037
		$plugins = array();
2038
		foreach ( $all_plugins as $path => $plugin_data ) {
2039
			$plugins[ $path ] = array(
2040
				'is_active' => in_array( $path, $active_plugins ),
2041
				'file'      => $path,
2042
				'name'      => $plugin_data['Name'],
2043
				'version'   => $plugin_data['Version'],
2044
				'author'    => $plugin_data['Author'],
2045
			);
2046
		}
2047
2048
		return $plugins;
2049
	}
2050
2051
	/**
2052
	 * Gets and parses theme data to send with the heartbeat data
2053
	 *
2054
	 * @since 3.8.1
2055
	 *
2056
	 * @return array Array of theme data
2057
	 */
2058
	public static function get_parsed_theme_data() {
2059
		$all_themes  = wp_get_themes( array( 'allowed' => true ) );
2060
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2061
2062
		$themes = array();
2063
		foreach ( $all_themes as $slug => $theme_data ) {
2064
			$theme_headers = array();
2065
			foreach ( $header_keys as $header_key ) {
2066
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2067
			}
2068
2069
			$themes[ $slug ] = array(
2070
				'is_active_theme' => $slug == wp_get_theme()->get_template(),
2071
				'slug'            => $slug,
2072
				'theme_root'      => $theme_data->get_theme_root_uri(),
2073
				'parent'          => $theme_data->parent(),
2074
				'headers'         => $theme_headers,
2075
			);
2076
		}
2077
2078
		return $themes;
2079
	}
2080
2081
	/**
2082
	 * Checks whether a specific plugin is active.
2083
	 *
2084
	 * We don't want to store these in a static variable, in case
2085
	 * there are switch_to_blog() calls involved.
2086
	 */
2087
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2088
		return in_array( $plugin, self::get_active_plugins() );
2089
	}
2090
2091
	/**
2092
	 * Check if Jetpack's Open Graph tags should be used.
2093
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2094
	 *
2095
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2096
	 * @action plugins_loaded
2097
	 * @return null
2098
	 */
2099
	public function check_open_graph() {
2100
		if ( in_array( 'publicize', self::get_active_modules() ) || in_array( 'sharedaddy', self::get_active_modules() ) ) {
2101
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2102
		}
2103
2104
		$active_plugins = self::get_active_plugins();
2105
2106
		if ( ! empty( $active_plugins ) ) {
2107
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2108
				if ( in_array( $plugin, $active_plugins ) ) {
2109
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2110
					break;
2111
				}
2112
			}
2113
		}
2114
2115
		/**
2116
		 * Allow the addition of Open Graph Meta Tags to all pages.
2117
		 *
2118
		 * @since 2.0.3
2119
		 *
2120
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2121
		 */
2122
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2123
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2124
		}
2125
	}
2126
2127
	/**
2128
	 * Check if Jetpack's Twitter tags should be used.
2129
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2130
	 *
2131
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2132
	 * @action plugins_loaded
2133
	 * @return null
2134
	 */
2135
	public function check_twitter_tags() {
2136
2137
		$active_plugins = self::get_active_plugins();
2138
2139
		if ( ! empty( $active_plugins ) ) {
2140
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2141
				if ( in_array( $plugin, $active_plugins ) ) {
2142
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2143
					break;
2144
				}
2145
			}
2146
		}
2147
2148
		/**
2149
		 * Allow Twitter Card Meta tags to be disabled.
2150
		 *
2151
		 * @since 2.6.0
2152
		 *
2153
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2154
		 */
2155
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2156
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2157
		}
2158
	}
2159
2160
	/**
2161
	 * Allows plugins to submit security reports.
2162
	 *
2163
	 * @param string $type         Report type (login_form, backup, file_scanning, spam)
2164
	 * @param string $plugin_file  Plugin __FILE__, so that we can pull plugin data
2165
	 * @param array  $args         See definitions above
2166
	 */
2167
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2168
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2169
	}
2170
2171
	/* Jetpack Options API */
2172
2173
	public static function get_option_names( $type = 'compact' ) {
2174
		return Jetpack_Options::get_option_names( $type );
2175
	}
2176
2177
	/**
2178
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2179
	 *
2180
	 * @param string $name    Option name
2181
	 * @param mixed  $default (optional)
2182
	 */
2183
	public static function get_option( $name, $default = false ) {
2184
		return Jetpack_Options::get_option( $name, $default );
2185
	}
2186
2187
	/**
2188
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2189
	 *
2190
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2191
	 * @param string $name  Option name
2192
	 * @param mixed  $value Option value
2193
	 */
2194
	public static function update_option( $name, $value ) {
2195
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2196
		return Jetpack_Options::update_option( $name, $value );
2197
	}
2198
2199
	/**
2200
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2201
	 *
2202
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2203
	 * @param array $array array( option name => option value, ... )
2204
	 */
2205
	public static function update_options( $array ) {
2206
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2207
		return Jetpack_Options::update_options( $array );
2208
	}
2209
2210
	/**
2211
	 * Deletes the given option.  May be passed multiple option names as an array.
2212
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2213
	 *
2214
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2215
	 * @param string|array $names
2216
	 */
2217
	public static function delete_option( $names ) {
2218
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2219
		return Jetpack_Options::delete_option( $names );
2220
	}
2221
2222
	/**
2223
	 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::update_user_token() instead.
2224
	 *
2225
	 * Enters a user token into the user_tokens option
2226
	 *
2227
	 * @param int    $user_id The user id.
2228
	 * @param string $token The user token.
2229
	 * @param bool   $is_master_user Whether the user is the master user.
2230
	 * @return bool
2231
	 */
2232
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2233
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::update_user_token' );
2234
		return Connection_Utils::update_user_token( $user_id, $token, $is_master_user );
2235
	}
2236
2237
	/**
2238
	 * Returns an array of all PHP files in the specified absolute path.
2239
	 * Equivalent to glob( "$absolute_path/*.php" ).
2240
	 *
2241
	 * @param string $absolute_path The absolute path of the directory to search.
2242
	 * @return array Array of absolute paths to the PHP files.
2243
	 */
2244
	public static function glob_php( $absolute_path ) {
2245
		if ( function_exists( 'glob' ) ) {
2246
			return glob( "$absolute_path/*.php" );
2247
		}
2248
2249
		$absolute_path = untrailingslashit( $absolute_path );
2250
		$files         = array();
2251
		if ( ! $dir = @opendir( $absolute_path ) ) {
2252
			return $files;
2253
		}
2254
2255
		while ( false !== $file = readdir( $dir ) ) {
2256
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2257
				continue;
2258
			}
2259
2260
			$file = "$absolute_path/$file";
2261
2262
			if ( ! is_file( $file ) ) {
2263
				continue;
2264
			}
2265
2266
			$files[] = $file;
2267
		}
2268
2269
		closedir( $dir );
2270
2271
		return $files;
2272
	}
2273
2274
	public static function activate_new_modules( $redirect = false ) {
2275
		if ( ! self::is_active() && ! ( new Status() )->is_offline_mode() ) {
2276
			return;
2277
		}
2278
2279
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2280 View Code Duplication
		if ( ! $jetpack_old_version ) {
2281
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2282
			/** This action is documented in class.jetpack.php */
2283
			do_action( 'updating_jetpack_version', $version, false );
2284
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2285
		}
2286
2287
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2288
2289
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2290
			return;
2291
		}
2292
2293
		$active_modules     = self::get_active_modules();
2294
		$reactivate_modules = array();
2295
		foreach ( $active_modules as $active_module ) {
2296
			$module = self::get_module( $active_module );
2297
			if ( ! isset( $module['changed'] ) ) {
2298
				continue;
2299
			}
2300
2301
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2302
				continue;
2303
			}
2304
2305
			$reactivate_modules[] = $active_module;
2306
			self::deactivate_module( $active_module );
2307
		}
2308
2309
		$new_version = JETPACK__VERSION . ':' . time();
2310
		/** This action is documented in class.jetpack.php */
2311
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2312
		Jetpack_Options::update_options(
2313
			array(
2314
				'version'     => $new_version,
2315
				'old_version' => $jetpack_old_version,
2316
			)
2317
		);
2318
2319
		self::state( 'message', 'modules_activated' );
2320
2321
		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...
2322
2323
		if ( $redirect ) {
2324
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2325
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2326
				$page = $_GET['page'];
2327
			}
2328
2329
			wp_safe_redirect( self::admin_url( 'page=' . $page ) );
2330
			exit;
2331
		}
2332
	}
2333
2334
	/**
2335
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2336
	 * Make sure to tuck away module "library" files in a sub-directory.
2337
	 */
2338
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2339
		static $modules = null;
2340
2341
		if ( ! isset( $modules ) ) {
2342
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2343
			// Use the cache if we're on the front-end and it's available...
2344
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2345
				$modules = $available_modules_option[ JETPACK__VERSION ];
2346
			} else {
2347
				$files = self::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2348
2349
				$modules = array();
2350
2351
				foreach ( $files as $file ) {
2352
					if ( ! $headers = self::get_module( $file ) ) {
2353
						continue;
2354
					}
2355
2356
					$modules[ self::get_module_slug( $file ) ] = $headers['introduced'];
2357
				}
2358
2359
				Jetpack_Options::update_option(
2360
					'available_modules',
2361
					array(
2362
						JETPACK__VERSION => $modules,
2363
					)
2364
				);
2365
			}
2366
		}
2367
2368
		/**
2369
		 * Filters the array of modules available to be activated.
2370
		 *
2371
		 * @since 2.4.0
2372
		 *
2373
		 * @param array $modules Array of available modules.
2374
		 * @param string $min_version Minimum version number required to use modules.
2375
		 * @param string $max_version Maximum version number required to use modules.
2376
		 */
2377
		$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...
2378
2379
		if ( ! $min_version && ! $max_version ) {
2380
			return array_keys( $mods );
2381
		}
2382
2383
		$r = array();
2384
		foreach ( $mods as $slug => $introduced ) {
2385
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2386
				continue;
2387
			}
2388
2389
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2390
				continue;
2391
			}
2392
2393
			$r[] = $slug;
2394
		}
2395
2396
		return $r;
2397
	}
2398
2399
	/**
2400
	 * Default modules loaded on activation.
2401
	 */
2402
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2403
		$return = array();
2404
2405
		foreach ( self::get_available_modules( $min_version, $max_version ) as $module ) {
2406
			$module_data = self::get_module( $module );
2407
2408
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2409
				case 'yes':
2410
					$return[] = $module;
2411
					break;
2412
				case 'public':
2413
					if ( Jetpack_Options::get_option( 'public' ) ) {
2414
						$return[] = $module;
2415
					}
2416
					break;
2417
				case 'no':
2418
				default:
2419
					break;
2420
			}
2421
		}
2422
		/**
2423
		 * Filters the array of default modules.
2424
		 *
2425
		 * @since 2.5.0
2426
		 *
2427
		 * @param array $return Array of default modules.
2428
		 * @param string $min_version Minimum version number required to use modules.
2429
		 * @param string $max_version Maximum version number required to use modules.
2430
		 */
2431
		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...
2432
	}
2433
2434
	/**
2435
	 * Checks activated modules during auto-activation to determine
2436
	 * if any of those modules are being deprecated.  If so, close
2437
	 * them out, and add any replacement modules.
2438
	 *
2439
	 * Runs at priority 99 by default.
2440
	 *
2441
	 * This is run late, so that it can still activate a module if
2442
	 * the new module is a replacement for another that the user
2443
	 * currently has active, even if something at the normal priority
2444
	 * would kibosh everything.
2445
	 *
2446
	 * @since 2.6
2447
	 * @uses jetpack_get_default_modules filter
2448
	 * @param array $modules
2449
	 * @return array
2450
	 */
2451
	function handle_deprecated_modules( $modules ) {
2452
		$deprecated_modules = array(
2453
			'debug'            => null,  // Closed out and moved to the debugger library.
2454
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2455
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2456
			'minileven'        => null,  // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2457
		);
2458
2459
		// Don't activate SSO if they never completed activating WPCC.
2460
		if ( self::is_module_active( 'wpcc' ) ) {
2461
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2462
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2463
				$deprecated_modules['wpcc'] = null;
2464
			}
2465
		}
2466
2467
		foreach ( $deprecated_modules as $module => $replacement ) {
2468
			if ( self::is_module_active( $module ) ) {
2469
				self::deactivate_module( $module );
2470
				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...
2471
					$modules[] = $replacement;
2472
				}
2473
			}
2474
		}
2475
2476
		return array_unique( $modules );
2477
	}
2478
2479
	/**
2480
	 * Checks activated plugins during auto-activation to determine
2481
	 * if any of those plugins are in the list with a corresponding module
2482
	 * that is not compatible with the plugin. The module will not be allowed
2483
	 * to auto-activate.
2484
	 *
2485
	 * @since 2.6
2486
	 * @uses jetpack_get_default_modules filter
2487
	 * @param array $modules
2488
	 * @return array
2489
	 */
2490
	function filter_default_modules( $modules ) {
2491
2492
		$active_plugins = self::get_active_plugins();
2493
2494
		if ( ! empty( $active_plugins ) ) {
2495
2496
			// For each module we'd like to auto-activate...
2497
			foreach ( $modules as $key => $module ) {
2498
				// If there are potential conflicts for it...
2499
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2500
					// For each potential conflict...
2501
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2502
						// If that conflicting plugin is active...
2503
						if ( in_array( $plugin, $active_plugins ) ) {
2504
							// Remove that item from being auto-activated.
2505
							unset( $modules[ $key ] );
2506
						}
2507
					}
2508
				}
2509
			}
2510
		}
2511
2512
		return $modules;
2513
	}
2514
2515
	/**
2516
	 * Extract a module's slug from its full path.
2517
	 */
2518
	public static function get_module_slug( $file ) {
2519
		return str_replace( '.php', '', basename( $file ) );
2520
	}
2521
2522
	/**
2523
	 * Generate a module's path from its slug.
2524
	 */
2525
	public static function get_module_path( $slug ) {
2526
		/**
2527
		 * Filters the path of a modules.
2528
		 *
2529
		 * @since 7.4.0
2530
		 *
2531
		 * @param array $return The absolute path to a module's root php file
2532
		 * @param string $slug The module slug
2533
		 */
2534
		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...
2535
	}
2536
2537
	/**
2538
	 * Load module data from module file. Headers differ from WordPress
2539
	 * plugin headers to avoid them being identified as standalone
2540
	 * plugins on the WordPress plugins page.
2541
	 */
2542
	public static function get_module( $module ) {
2543
		$headers = array(
2544
			'name'                      => 'Module Name',
2545
			'description'               => 'Module Description',
2546
			'sort'                      => 'Sort Order',
2547
			'recommendation_order'      => 'Recommendation Order',
2548
			'introduced'                => 'First Introduced',
2549
			'changed'                   => 'Major Changes In',
2550
			'deactivate'                => 'Deactivate',
2551
			'free'                      => 'Free',
2552
			'requires_connection'       => 'Requires Connection',
2553
			'auto_activate'             => 'Auto Activate',
2554
			'module_tags'               => 'Module Tags',
2555
			'feature'                   => 'Feature',
2556
			'additional_search_queries' => 'Additional Search Queries',
2557
			'plan_classes'              => 'Plans',
2558
		);
2559
2560
		$file = self::get_module_path( self::get_module_slug( $module ) );
2561
2562
		$mod = self::get_file_data( $file, $headers );
2563
		if ( empty( $mod['name'] ) ) {
2564
			return false;
2565
		}
2566
2567
		$mod['sort']                 = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2568
		$mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2569
		$mod['deactivate']           = empty( $mod['deactivate'] );
2570
		$mod['free']                 = empty( $mod['free'] );
2571
		$mod['requires_connection']  = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2572
2573
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2574
			$mod['auto_activate'] = 'No';
2575
		} else {
2576
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2577
		}
2578
2579
		if ( $mod['module_tags'] ) {
2580
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2581
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2582
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2583
		} else {
2584
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2585
		}
2586
2587 View Code Duplication
		if ( $mod['plan_classes'] ) {
2588
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2589
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2590
		} else {
2591
			$mod['plan_classes'] = array( 'free' );
2592
		}
2593
2594 View Code Duplication
		if ( $mod['feature'] ) {
2595
			$mod['feature'] = explode( ',', $mod['feature'] );
2596
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2597
		} else {
2598
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2599
		}
2600
2601
		/**
2602
		 * Filters the feature array on a module.
2603
		 *
2604
		 * This filter allows you to control where each module is filtered: Recommended,
2605
		 * and the default "Other" listing.
2606
		 *
2607
		 * @since 3.5.0
2608
		 *
2609
		 * @param array   $mod['feature'] The areas to feature this module:
2610
		 *     'Recommended' shows on the main Jetpack admin screen.
2611
		 *     'Other' should be the default if no other value is in the array.
2612
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2613
		 * @param array   $mod All the currently assembled module data.
2614
		 */
2615
		$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...
2616
2617
		/**
2618
		 * Filter the returned data about a module.
2619
		 *
2620
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2621
		 * so please be careful.
2622
		 *
2623
		 * @since 3.6.0
2624
		 *
2625
		 * @param array   $mod    The details of the requested module.
2626
		 * @param string  $module The slug of the module, e.g. sharedaddy
2627
		 * @param string  $file   The path to the module source file.
2628
		 */
2629
		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...
2630
	}
2631
2632
	/**
2633
	 * Like core's get_file_data implementation, but caches the result.
2634
	 */
2635
	public static function get_file_data( $file, $headers ) {
2636
		// Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2637
		$file_name = basename( $file );
2638
2639
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2640
2641
		$file_data_option = get_transient( $cache_key );
2642
2643
		if ( ! is_array( $file_data_option ) ) {
2644
			delete_transient( $cache_key );
2645
			$file_data_option = false;
2646
		}
2647
2648
		if ( false === $file_data_option ) {
2649
			$file_data_option = array();
2650
		}
2651
2652
		$key           = md5( $file_name . serialize( $headers ) );
2653
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2654
2655
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2656
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2657
			return $file_data_option[ $key ];
2658
		}
2659
2660
		$data = get_file_data( $file, $headers );
2661
2662
		$file_data_option[ $key ] = $data;
2663
2664
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2665
2666
		return $data;
2667
	}
2668
2669
2670
	/**
2671
	 * Return translated module tag.
2672
	 *
2673
	 * @param string $tag Tag as it appears in each module heading.
2674
	 *
2675
	 * @return mixed
2676
	 */
2677
	public static function translate_module_tag( $tag ) {
2678
		return jetpack_get_module_i18n_tag( $tag );
2679
	}
2680
2681
	/**
2682
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2683
	 *
2684
	 * @since 3.9.2
2685
	 *
2686
	 * @param array $modules
2687
	 *
2688
	 * @return string|void
2689
	 */
2690
	public static function get_translated_modules( $modules ) {
2691
		foreach ( $modules as $index => $module ) {
2692
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2693
			if ( isset( $module['name'] ) ) {
2694
				$modules[ $index ]['name'] = $i18n_module['name'];
2695
			}
2696
			if ( isset( $module['description'] ) ) {
2697
				$modules[ $index ]['description']       = $i18n_module['description'];
2698
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2699
			}
2700
		}
2701
		return $modules;
2702
	}
2703
2704
	/**
2705
	 * Get a list of activated modules as an array of module slugs.
2706
	 */
2707
	public static function get_active_modules() {
2708
		$active = Jetpack_Options::get_option( 'active_modules' );
2709
2710
		if ( ! is_array( $active ) ) {
2711
			$active = array();
2712
		}
2713
2714
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2715
			$active[] = 'vaultpress';
2716
		} else {
2717
			$active = array_diff( $active, array( 'vaultpress' ) );
2718
		}
2719
2720
		// If protect is active on the main site of a multisite, it should be active on all sites.
2721
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2722
			$active[] = 'protect';
2723
		}
2724
2725
		/**
2726
		 * Allow filtering of the active modules.
2727
		 *
2728
		 * Gives theme and plugin developers the power to alter the modules that
2729
		 * are activated on the fly.
2730
		 *
2731
		 * @since 5.8.0
2732
		 *
2733
		 * @param array $active Array of active module slugs.
2734
		 */
2735
		$active = apply_filters( 'jetpack_active_modules', $active );
2736
2737
		return array_unique( $active );
2738
	}
2739
2740
	/**
2741
	 * Check whether or not a Jetpack module is active.
2742
	 *
2743
	 * @param string $module The slug of a Jetpack module.
2744
	 * @return bool
2745
	 *
2746
	 * @static
2747
	 */
2748
	public static function is_module_active( $module ) {
2749
		return in_array( $module, self::get_active_modules() );
2750
	}
2751
2752
	public static function is_module( $module ) {
2753
		return ! empty( $module ) && ! validate_file( $module, self::get_available_modules() );
2754
	}
2755
2756
	/**
2757
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2758
	 *
2759
	 * @param bool $catch True to start catching, False to stop.
2760
	 *
2761
	 * @static
2762
	 */
2763
	public static function catch_errors( $catch ) {
2764
		static $display_errors, $error_reporting;
2765
2766
		if ( $catch ) {
2767
			$display_errors  = @ini_set( 'display_errors', 1 );
2768
			$error_reporting = @error_reporting( E_ALL );
2769
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2770
		} else {
2771
			@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...
2772
			@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...
2773
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2774
		}
2775
	}
2776
2777
	/**
2778
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2779
	 */
2780
	public static function catch_errors_on_shutdown() {
2781
		self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2782
	}
2783
2784
	/**
2785
	 * Rewrite any string to make paths easier to read.
2786
	 *
2787
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2788
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2789
	 *
2790
	 * @param $string
2791
	 * @return mixed
2792
	 */
2793
	public static function alias_directories( $string ) {
2794
		// ABSPATH has a trailing slash.
2795
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2796
		// WP_CONTENT_DIR does not have a trailing slash.
2797
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2798
2799
		return $string;
2800
	}
2801
2802
	public static function activate_default_modules(
2803
		$min_version = false,
2804
		$max_version = false,
2805
		$other_modules = array(),
2806
		$redirect = null,
2807
		$send_state_messages = null
2808
	) {
2809
		$jetpack = self::init();
2810
2811
		if ( is_null( $redirect ) ) {
2812
			if (
2813
				( defined( 'REST_REQUEST' ) && REST_REQUEST )
2814
			||
2815
				( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
2816
			||
2817
				( defined( 'WP_CLI' ) && WP_CLI )
2818
			||
2819
				( defined( 'DOING_CRON' ) && DOING_CRON )
2820
			||
2821
				( defined( 'DOING_AJAX' ) && DOING_AJAX )
2822
			) {
2823
				$redirect = false;
2824
			} elseif ( is_admin() ) {
2825
				$redirect = true;
2826
			} else {
2827
				$redirect = false;
2828
			}
2829
		}
2830
2831
		if ( is_null( $send_state_messages ) ) {
2832
			$send_state_messages = current_user_can( 'jetpack_activate_modules' );
2833
		}
2834
2835
		$modules = self::get_default_modules( $min_version, $max_version );
2836
		$modules = array_merge( $other_modules, $modules );
2837
2838
		// Look for standalone plugins and disable if active.
2839
2840
		$to_deactivate = array();
2841
		foreach ( $modules as $module ) {
2842
			if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2843
				$to_deactivate[ $module ] = $jetpack->plugins_to_deactivate[ $module ];
2844
			}
2845
		}
2846
2847
		$deactivated = array();
2848
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2849
			list( $probable_file, $probable_title ) = $deactivate_me;
2850
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2851
				$deactivated[] = $module;
2852
			}
2853
		}
2854
2855
		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...
2856
			if ( $send_state_messages ) {
2857
				self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2858
			}
2859
2860
			if ( $redirect ) {
2861
				$url = add_query_arg(
2862
					array(
2863
						'action'   => 'activate_default_modules',
2864
						'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2865
					),
2866
					add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2867
				);
2868
				wp_safe_redirect( $url );
2869
				exit;
2870
			}
2871
		}
2872
2873
		/**
2874
		 * Fires before default modules are activated.
2875
		 *
2876
		 * @since 1.9.0
2877
		 *
2878
		 * @param string $min_version Minimum version number required to use modules.
2879
		 * @param string $max_version Maximum version number required to use modules.
2880
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2881
		 */
2882
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2883
2884
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2885
		if ( $send_state_messages ) {
2886
			self::restate();
2887
			self::catch_errors( true );
2888
		}
2889
2890
		$active = self::get_active_modules();
2891
2892
		foreach ( $modules as $module ) {
2893
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2894
				$active[] = $module;
2895
				self::update_active_modules( $active );
2896
				continue;
2897
			}
2898
2899
			if ( $send_state_messages && in_array( $module, $active ) ) {
2900
				$module_info = self::get_module( $module );
2901 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2902
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2903
					if ( $active_state = self::state( $state ) ) {
2904
						$active_state = explode( ',', $active_state );
2905
					} else {
2906
						$active_state = array();
2907
					}
2908
					$active_state[] = $module;
2909
					self::state( $state, implode( ',', $active_state ) );
2910
				}
2911
				continue;
2912
			}
2913
2914
			$file = self::get_module_path( $module );
2915
			if ( ! file_exists( $file ) ) {
2916
				continue;
2917
			}
2918
2919
			// we'll override this later if the plugin can be included without fatal error
2920
			if ( $redirect ) {
2921
				wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
2922
			}
2923
2924
			if ( $send_state_messages ) {
2925
				self::state( 'error', 'module_activation_failed' );
2926
				self::state( 'module', $module );
2927
			}
2928
2929
			ob_start();
2930
			require_once $file;
2931
2932
			$active[] = $module;
2933
2934 View Code Duplication
			if ( $send_state_messages ) {
2935
2936
				$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2937
				if ( $active_state = self::state( $state ) ) {
2938
					$active_state = explode( ',', $active_state );
2939
				} else {
2940
					$active_state = array();
2941
				}
2942
				$active_state[] = $module;
2943
				self::state( $state, implode( ',', $active_state ) );
2944
			}
2945
2946
			self::update_active_modules( $active );
2947
2948
			ob_end_clean();
2949
		}
2950
2951
		if ( $send_state_messages ) {
2952
			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...
2953
			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...
2954
		}
2955
2956
		self::catch_errors( false );
2957
		/**
2958
		 * Fires when default modules are activated.
2959
		 *
2960
		 * @since 1.9.0
2961
		 *
2962
		 * @param string $min_version Minimum version number required to use modules.
2963
		 * @param string $max_version Maximum version number required to use modules.
2964
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2965
		 */
2966
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2967
	}
2968
2969
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2970
		/**
2971
		 * Fires before a module is activated.
2972
		 *
2973
		 * @since 2.6.0
2974
		 *
2975
		 * @param string $module Module slug.
2976
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2977
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2978
		 */
2979
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2980
2981
		$jetpack = self::init();
2982
2983
		if ( ! strlen( $module ) ) {
2984
			return false;
2985
		}
2986
2987
		if ( ! self::is_module( $module ) ) {
2988
			return false;
2989
		}
2990
2991
		// If it's already active, then don't do it again
2992
		$active = self::get_active_modules();
2993
		foreach ( $active as $act ) {
2994
			if ( $act == $module ) {
2995
				return true;
2996
			}
2997
		}
2998
2999
		$module_data = self::get_module( $module );
3000
3001
		$is_offline_mode = ( new Status() )->is_offline_mode();
3002
		if ( ! self::is_active() ) {
3003
			if ( ! $is_offline_mode && ! self::is_onboarding() ) {
3004
				return false;
3005
			}
3006
3007
			// If we're not connected but in offline mode, make sure the module doesn't require a connection.
3008
			if ( $is_offline_mode && $module_data['requires_connection'] ) {
3009
				return false;
3010
			}
3011
		}
3012
3013
		// Check and see if the old plugin is active
3014
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3015
			// Deactivate the old plugin
3016
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
3017
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
3018
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
3019
				self::state( 'deactivated_plugins', $module );
3020
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
3021
				exit;
3022
			}
3023
		}
3024
3025
		// Protect won't work with mis-configured IPs
3026
		if ( 'protect' === $module ) {
3027
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
3028
			if ( ! jetpack_protect_get_ip() ) {
3029
				self::state( 'message', 'protect_misconfigured_ip' );
3030
				return false;
3031
			}
3032
		}
3033
3034
		if ( ! Jetpack_Plan::supports( $module ) ) {
3035
			return false;
3036
		}
3037
3038
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
3039
		self::state( 'module', $module );
3040
		self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
3041
3042
		self::catch_errors( true );
3043
		ob_start();
3044
		require self::get_module_path( $module );
3045
		/** This action is documented in class.jetpack.php */
3046
		do_action( 'jetpack_activate_module', $module );
3047
		$active[] = $module;
3048
		self::update_active_modules( $active );
3049
3050
		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...
3051
		ob_end_clean();
3052
		self::catch_errors( false );
3053
3054
		if ( $redirect ) {
3055
			wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3056
		}
3057
		if ( $exit ) {
3058
			exit;
3059
		}
3060
		return true;
3061
	}
3062
3063
	function activate_module_actions( $module ) {
3064
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3065
	}
3066
3067
	public static function deactivate_module( $module ) {
3068
		/**
3069
		 * Fires when a module is deactivated.
3070
		 *
3071
		 * @since 1.9.0
3072
		 *
3073
		 * @param string $module Module slug.
3074
		 */
3075
		do_action( 'jetpack_pre_deactivate_module', $module );
3076
3077
		$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...
3078
3079
		$active = self::get_active_modules();
3080
		$new    = array_filter( array_diff( $active, (array) $module ) );
3081
3082
		return self::update_active_modules( $new );
3083
	}
3084
3085
	public static function enable_module_configurable( $module ) {
3086
		$module = self::get_module_slug( $module );
3087
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3088
	}
3089
3090
	/**
3091
	 * Composes a module configure URL. It uses Jetpack settings search as default value
3092
	 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3093
	 *
3094
	 * @param string $module Module slug
3095
	 * @return string $url module configuration URL
3096
	 */
3097
	public static function module_configuration_url( $module ) {
3098
		$module      = self::get_module_slug( $module );
3099
		$default_url = self::admin_url() . "#/settings?term=$module";
3100
		/**
3101
		 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3102
		 *
3103
		 * @since 6.9.0
3104
		 *
3105
		 * @param string $default_url Default url, which redirects to jetpack settings page.
3106
		 */
3107
		$url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3108
3109
		return $url;
3110
	}
3111
3112
	/* Installation */
3113
	public static function bail_on_activation( $message, $deactivate = true ) {
3114
		?>
3115
<!doctype html>
3116
<html>
3117
<head>
3118
<meta charset="<?php bloginfo( 'charset' ); ?>">
3119
<style>
3120
* {
3121
	text-align: center;
3122
	margin: 0;
3123
	padding: 0;
3124
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3125
}
3126
p {
3127
	margin-top: 1em;
3128
	font-size: 18px;
3129
}
3130
</style>
3131
<body>
3132
<p><?php echo esc_html( $message ); ?></p>
3133
</body>
3134
</html>
3135
		<?php
3136
		if ( $deactivate ) {
3137
			$plugins = get_option( 'active_plugins' );
3138
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3139
			$update  = false;
3140
			foreach ( $plugins as $i => $plugin ) {
3141
				if ( $plugin === $jetpack ) {
3142
					$plugins[ $i ] = false;
3143
					$update        = true;
3144
				}
3145
			}
3146
3147
			if ( $update ) {
3148
				update_option( 'active_plugins', array_filter( $plugins ) );
3149
			}
3150
		}
3151
		exit;
3152
	}
3153
3154
	/**
3155
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3156
	 *
3157
	 * @static
3158
	 */
3159
	public static function plugin_activation( $network_wide ) {
3160
		Jetpack_Options::update_option( 'activated', 1 );
3161
3162
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3163
			self::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3164
		}
3165
3166
		if ( $network_wide ) {
3167
			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...
3168
		}
3169
3170
		// For firing one-off events (notices) immediately after activation
3171
		set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS );
3172
3173
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3174
3175
		Health::on_jetpack_activated();
3176
3177
		self::plugin_initialize();
3178
	}
3179
3180
	public static function get_activation_source( $referer_url ) {
3181
3182
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3183
			return array( 'wp-cli', null );
3184
		}
3185
3186
		$referer = wp_parse_url( $referer_url );
3187
3188
		$source_type  = 'unknown';
3189
		$source_query = null;
3190
3191
		if ( ! is_array( $referer ) ) {
3192
			return array( $source_type, $source_query );
3193
		}
3194
3195
		$plugins_path         = wp_parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_PATH.

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...
3196
		$plugins_install_path = wp_parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_PATH.

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...
3197
3198
		if ( isset( $referer['query'] ) ) {
3199
			parse_str( $referer['query'], $query_parts );
3200
		} else {
3201
			$query_parts = array();
3202
		}
3203
3204
		if ( $plugins_path === $referer['path'] ) {
3205
			$source_type = 'list';
3206
		} elseif ( $plugins_install_path === $referer['path'] ) {
3207
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3208
			switch ( $tab ) {
3209
				case 'popular':
3210
					$source_type = 'popular';
3211
					break;
3212
				case 'recommended':
3213
					$source_type = 'recommended';
3214
					break;
3215
				case 'favorites':
3216
					$source_type = 'favorites';
3217
					break;
3218
				case 'search':
3219
					$source_type  = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3220
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3221
					break;
3222
				default:
3223
					$source_type = 'featured';
3224
			}
3225
		}
3226
3227
		return array( $source_type, $source_query );
3228
	}
3229
3230
	/**
3231
	 * Runs before bumping version numbers up to a new version
3232
	 *
3233
	 * @param string $version    Version:timestamp.
3234
	 * @param string $old_version Old Version:timestamp or false if not set yet.
3235
	 */
3236
	public static function do_version_bump( $version, $old_version ) {
3237
		if ( $old_version ) { // For existing Jetpack installations.
3238
3239
			// If a front end page is visited after the update, the 'wp' action will fire.
3240
			add_action( 'wp', 'Jetpack::set_update_modal_display' );
3241
3242
			// If an admin page is visited after the update, the 'current_screen' action will fire.
3243
			add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3244
		}
3245
	}
3246
3247
	/**
3248
	 * Sets the display_update_modal state.
3249
	 */
3250
	public static function set_update_modal_display() {
3251
		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...
3252
	}
3253
3254
	/**
3255
	 * Sets the internal version number and activation state.
3256
	 *
3257
	 * @static
3258
	 */
3259
	public static function plugin_initialize() {
3260
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3261
			Jetpack_Options::update_option( 'activated', 2 );
3262
		}
3263
3264 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3265
			$version = $old_version = JETPACK__VERSION . ':' . time();
3266
			/** This action is documented in class.jetpack.php */
3267
			do_action( 'updating_jetpack_version', $version, false );
3268
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3269
		}
3270
3271
		self::load_modules();
3272
3273
		Jetpack_Options::delete_option( 'do_activate' );
3274
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3275
	}
3276
3277
	/**
3278
	 * Removes all connection options
3279
	 *
3280
	 * @static
3281
	 */
3282
	public static function plugin_deactivation() {
3283
		require_once ABSPATH . '/wp-admin/includes/plugin.php';
3284
		$tracking = new Tracking();
3285
		$tracking->record_user_event( 'deactivate_plugin', array() );
3286
		if ( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3287
			Jetpack_Network::init()->deactivate();
3288
		} else {
3289
			self::disconnect( false );
3290
			// Jetpack_Heartbeat::init()->deactivate();
3291
		}
3292
	}
3293
3294
	/**
3295
	 * Disconnects from the Jetpack servers.
3296
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3297
	 *
3298
	 * @static
3299
	 */
3300
	public static function disconnect( $update_activated_state = true ) {
3301
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3302
		$connection = self::connection();
3303
		$connection->clean_nonces( true );
3304
3305
		// If the site is in an IDC because sync is not allowed,
3306
		// let's make sure to not disconnect the production site.
3307
		if ( ! self::validate_sync_error_idc_option() ) {
3308
			$tracking = new Tracking();
3309
			$tracking->record_user_event( 'disconnect_site', array() );
3310
3311
			$connection->disconnect_site_wpcom( true );
3312
		}
3313
3314
		$connection->delete_all_connection_tokens( true );
3315
		Jetpack_IDC::clear_all_idc_options();
3316
3317
		if ( $update_activated_state ) {
3318
			Jetpack_Options::update_option( 'activated', 4 );
3319
		}
3320
3321
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3322
			// Check then record unique disconnection if site has never been disconnected previously
3323
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3324
				$jetpack_unique_connection['disconnected'] = 1;
3325
			} else {
3326
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3327
					// track unique disconnect
3328
					$jetpack = self::init();
3329
3330
					$jetpack->stat( 'connections', 'unique-disconnect' );
3331
					$jetpack->do_stats( 'server_side' );
3332
				}
3333
				// increment number of times disconnected
3334
				$jetpack_unique_connection['disconnected'] += 1;
3335
			}
3336
3337
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3338
		}
3339
3340
		// Delete all the sync related data. Since it could be taking up space.
3341
		Sender::get_instance()->uninstall();
3342
3343
	}
3344
3345
	/**
3346
	 * Unlinks the current user from the linked WordPress.com user.
3347
	 *
3348
	 * @deprecated since 7.7
3349
	 * @see Automattic\Jetpack\Connection\Manager::disconnect_user()
3350
	 *
3351
	 * @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...
3352
	 * @return Boolean Whether the disconnection of the user was successful.
3353
	 */
3354
	public static function unlink_user( $user_id = null ) {
3355
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::disconnect_user' );
3356
		return Connection_Manager::disconnect_user( $user_id );
3357
	}
3358
3359
	/**
3360
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3361
	 */
3362
	public static function try_registration() {
3363
		$terms_of_service = new Terms_Of_Service();
3364
		// The user has agreed to the TOS at some point by now.
3365
		$terms_of_service->agree();
3366
3367
		// Let's get some testing in beta versions and such.
3368
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3369
			// Before attempting to connect, let's make sure that the domains are viable.
3370
			$domains_to_check = array_unique(
3371
				array(
3372
					'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_HOST.

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...
3373
					'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_HOST.

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...
3374
				)
3375
			);
3376
			foreach ( $domains_to_check as $domain ) {
3377
				$result = self::connection()->is_usable_domain( $domain );
0 ignored issues
show
$domain is of type array<string,string>|false, 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...
3378
				if ( is_wp_error( $result ) ) {
3379
					return $result;
3380
				}
3381
			}
3382
		}
3383
3384
		$result = self::register();
3385
3386
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3387
		if ( ! $result || is_wp_error( $result ) ) {
3388
			return $result;
3389
		} else {
3390
			return true;
3391
		}
3392
	}
3393
3394
	/**
3395
	 * Tracking an internal event log. Try not to put too much chaff in here.
3396
	 *
3397
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3398
	 */
3399
	public static function log( $code, $data = null ) {
3400
		// only grab the latest 200 entries
3401
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3402
3403
		// Append our event to the log
3404
		$log_entry = array(
3405
			'time'    => time(),
3406
			'user_id' => get_current_user_id(),
3407
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3408
			'code'    => $code,
3409
		);
3410
		// Don't bother storing it unless we've got some.
3411
		if ( ! is_null( $data ) ) {
3412
			$log_entry['data'] = $data;
3413
		}
3414
		$log[] = $log_entry;
3415
3416
		// Try add_option first, to make sure it's not autoloaded.
3417
		// @todo: Add an add_option method to Jetpack_Options
3418
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3419
			Jetpack_Options::update_option( 'log', $log );
3420
		}
3421
3422
		/**
3423
		 * Fires when Jetpack logs an internal event.
3424
		 *
3425
		 * @since 3.0.0
3426
		 *
3427
		 * @param array $log_entry {
3428
		 *  Array of details about the log entry.
3429
		 *
3430
		 *  @param string time Time of the event.
3431
		 *  @param int user_id ID of the user who trigerred the event.
3432
		 *  @param int blog_id Jetpack Blog ID.
3433
		 *  @param string code Unique name for the event.
3434
		 *  @param string data Data about the event.
3435
		 * }
3436
		 */
3437
		do_action( 'jetpack_log_entry', $log_entry );
3438
	}
3439
3440
	/**
3441
	 * Get the internal event log.
3442
	 *
3443
	 * @param $event (string) - only return the specific log events
3444
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3445
	 *
3446
	 * @return array of log events || WP_Error for invalid params
3447
	 */
3448
	public static function get_log( $event = false, $num = false ) {
3449
		if ( $event && ! is_string( $event ) ) {
3450
			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...
3451
		}
3452
3453
		if ( $num && ! is_numeric( $num ) ) {
3454
			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...
3455
		}
3456
3457
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3458
3459
		// If nothing set - act as it did before, otherwise let's start customizing the output
3460
		if ( ! $num && ! $event ) {
3461
			return $entire_log;
3462
		} else {
3463
			$entire_log = array_reverse( $entire_log );
3464
		}
3465
3466
		$custom_log_output = array();
3467
3468
		if ( $event ) {
3469
			foreach ( $entire_log as $log_event ) {
3470
				if ( $event == $log_event['code'] ) {
3471
					$custom_log_output[] = $log_event;
3472
				}
3473
			}
3474
		} else {
3475
			$custom_log_output = $entire_log;
3476
		}
3477
3478
		if ( $num ) {
3479
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3480
		}
3481
3482
		return $custom_log_output;
3483
	}
3484
3485
	/**
3486
	 * Log modification of important settings.
3487
	 */
3488
	public static function log_settings_change( $option, $old_value, $value ) {
3489
		switch ( $option ) {
3490
			case 'jetpack_sync_non_public_post_stati':
3491
				self::log( $option, $value );
3492
				break;
3493
		}
3494
	}
3495
3496
	/**
3497
	 * Return stat data for WPCOM sync
3498
	 */
3499
	public static function get_stat_data( $encode = true, $extended = true ) {
3500
		$data = Jetpack_Heartbeat::generate_stats_array();
3501
3502
		if ( $extended ) {
3503
			$additional_data = self::get_additional_stat_data();
3504
			$data            = array_merge( $data, $additional_data );
3505
		}
3506
3507
		if ( $encode ) {
3508
			return json_encode( $data );
3509
		}
3510
3511
		return $data;
3512
	}
3513
3514
	/**
3515
	 * Get additional stat data to sync to WPCOM
3516
	 */
3517
	public static function get_additional_stat_data( $prefix = '' ) {
3518
		$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...
3519
		$return[ "{$prefix}plugins-extra" ] = self::get_parsed_plugin_data();
3520
		$return[ "{$prefix}users" ]         = (int) self::get_site_user_count();
3521
		$return[ "{$prefix}site-count" ]    = 0;
3522
3523
		if ( function_exists( 'get_blog_count' ) ) {
3524
			$return[ "{$prefix}site-count" ] = get_blog_count();
3525
		}
3526
		return $return;
3527
	}
3528
3529
	private static function get_site_user_count() {
3530
		global $wpdb;
3531
3532
		if ( function_exists( 'wp_is_large_network' ) ) {
3533
			if ( wp_is_large_network( 'users' ) ) {
3534
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3535
			}
3536
		}
3537
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3538
			// It wasn't there, so regenerate the data and save the transient
3539
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3540
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3541
		}
3542
		return $user_count;
3543
	}
3544
3545
	/* Admin Pages */
3546
3547
	function admin_init() {
3548
		// If the plugin is not connected, display a connect message.
3549
		if (
3550
			// the plugin was auto-activated and needs its candy
3551
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3552
		||
3553
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3554
			! Jetpack_Options::get_option( 'activated' )
3555
		) {
3556
			self::plugin_initialize();
3557
		}
3558
3559
		$is_offline_mode = ( new Status() )->is_offline_mode();
3560
		if ( ! self::is_active() && ! $is_offline_mode ) {
3561
			Jetpack_Connection_Banner::init();
3562
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3563
			// Upgrade: 1.1 -> 1.1.1
3564
			// Check and see if host can verify the Jetpack servers' SSL certificate
3565
			$args       = array();
3566
			$connection = self::connection();
3567
			Client::_wp_remote_request(
3568
				Connection_Utils::fix_url_for_bad_hosts( $connection->api_url( 'test' ) ),
3569
				$args,
3570
				true
3571
			);
3572
		}
3573
3574
		Jetpack_Wizard_Banner::init();
3575
3576
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3577
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3578
		}
3579
3580
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3581
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3582
		add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3583
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3584
3585
		if ( self::is_active() || $is_offline_mode ) {
3586
			// Artificially throw errors in certain specific cases during plugin activation.
3587
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3588
		}
3589
3590
		// Add custom column in wp-admin/users.php to show whether user is linked.
3591
		add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3592
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3593
		add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3594
	}
3595
3596
	function admin_body_class( $admin_body_class = '' ) {
3597
		$classes = explode( ' ', trim( $admin_body_class ) );
3598
3599
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3600
3601
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3602
		return " $admin_body_class ";
3603
	}
3604
3605
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3606
		return $admin_body_class . ' jetpack-pagestyles ';
3607
	}
3608
3609
	/**
3610
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3611
	 * This function artificially throws errors for such cases (per a specific list).
3612
	 *
3613
	 * @param string $plugin The activated plugin.
3614
	 */
3615
	function throw_error_on_activate_plugin( $plugin ) {
3616
		$active_modules = self::get_active_modules();
3617
3618
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3619
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3620
			$throw = false;
3621
3622
			// Try and make sure it really was the stats plugin
3623
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3624
				if ( 'stats.php' == basename( $plugin ) ) {
3625
					$throw = true;
3626
				}
3627
			} else {
3628
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3629
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3630
					$throw = true;
3631
				}
3632
			}
3633
3634
			if ( $throw ) {
3635
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3636
			}
3637
		}
3638
	}
3639
3640
	function intercept_plugin_error_scrape_init() {
3641
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3642
	}
3643
3644
	function intercept_plugin_error_scrape( $action, $result ) {
3645
		if ( ! $result ) {
3646
			return;
3647
		}
3648
3649
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3650
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3651
				self::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3652
			}
3653
		}
3654
	}
3655
3656
	/**
3657
	 * Register the remote file upload request handlers, if needed.
3658
	 *
3659
	 * @access public
3660
	 */
3661
	public function add_remote_request_handlers() {
3662
		// Remote file uploads are allowed only via AJAX requests.
3663
		if ( ! is_admin() || ! Constants::get_constant( 'DOING_AJAX' ) ) {
3664
			return;
3665
		}
3666
3667
		// Remote file uploads are allowed only for a set of specific AJAX actions.
3668
		$remote_request_actions = array(
3669
			'jetpack_upload_file',
3670
			'jetpack_update_file',
3671
		);
3672
3673
		// phpcs:ignore WordPress.Security.NonceVerification
3674
		if ( ! isset( $_POST['action'] ) || ! in_array( $_POST['action'], $remote_request_actions, true ) ) {
3675
			return;
3676
		}
3677
3678
		// Require Jetpack authentication for the remote file upload AJAX requests.
3679
		if ( ! $this->connection_manager ) {
3680
			$this->connection_manager = new Connection_Manager();
3681
		}
3682
3683
		$this->connection_manager->require_jetpack_authentication();
3684
3685
		// Register the remote file upload AJAX handlers.
3686
		foreach ( $remote_request_actions as $action ) {
3687
			add_action( "wp_ajax_nopriv_{$action}", array( $this, 'remote_request_handlers' ) );
3688
		}
3689
	}
3690
3691
	/**
3692
	 * Handler for Jetpack remote file uploads.
3693
	 *
3694
	 * @access public
3695
	 */
3696
	public function remote_request_handlers() {
3697
		$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...
3698
3699
		switch ( current_filter() ) {
3700
			case 'wp_ajax_nopriv_jetpack_upload_file':
3701
				$response = $this->upload_handler();
3702
				break;
3703
3704
			case 'wp_ajax_nopriv_jetpack_update_file':
3705
				$response = $this->upload_handler( true );
3706
				break;
3707
			default:
3708
				$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...
3709
				break;
3710
		}
3711
3712
		if ( ! $response ) {
3713
			$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...
3714
		}
3715
3716
		if ( is_wp_error( $response ) ) {
3717
			$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...
3718
			$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...
3719
			$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...
3720
3721
			if ( ! is_int( $status_code ) ) {
3722
				$status_code = 400;
3723
			}
3724
3725
			status_header( $status_code );
3726
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3727
		}
3728
3729
		status_header( 200 );
3730
		if ( true === $response ) {
3731
			exit;
3732
		}
3733
3734
		die( json_encode( (object) $response ) );
3735
	}
3736
3737
	/**
3738
	 * Uploads a file gotten from the global $_FILES.
3739
	 * If `$update_media_item` is true and `post_id` is defined
3740
	 * the attachment file of the media item (gotten through of the post_id)
3741
	 * will be updated instead of add a new one.
3742
	 *
3743
	 * @param  boolean $update_media_item - update media attachment
3744
	 * @return array - An array describing the uploadind files process
3745
	 */
3746
	function upload_handler( $update_media_item = false ) {
3747
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3748
			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...
3749
		}
3750
3751
		$user = wp_authenticate( '', '' );
3752
		if ( ! $user || is_wp_error( $user ) ) {
3753
			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...
3754
		}
3755
3756
		wp_set_current_user( $user->ID );
3757
3758
		if ( ! current_user_can( 'upload_files' ) ) {
3759
			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...
3760
		}
3761
3762
		if ( empty( $_FILES ) ) {
3763
			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...
3764
		}
3765
3766
		foreach ( array_keys( $_FILES ) as $files_key ) {
3767
			if ( ! isset( $_POST[ "_jetpack_file_hmac_{$files_key}" ] ) ) {
3768
				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...
3769
			}
3770
		}
3771
3772
		$media_keys = array_keys( $_FILES['media'] );
3773
3774
		$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...
3775
		if ( ! $token || is_wp_error( $token ) ) {
3776
			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...
3777
		}
3778
3779
		$uploaded_files = array();
3780
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3781
		unset( $GLOBALS['post'] );
3782
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3783
			$file = array();
3784
			foreach ( $media_keys as $media_key ) {
3785
				$file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ];
3786
			}
3787
3788
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][ $index ] );
3789
3790
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3791
			if ( $hmac_provided !== $hmac_file ) {
3792
				$uploaded_files[ $index ] = (object) array(
3793
					'error'             => 'invalid_hmac',
3794
					'error_description' => 'The corresponding HMAC for this file does not match',
3795
				);
3796
				continue;
3797
			}
3798
3799
			$_FILES['.jetpack.upload.'] = $file;
3800
			$post_id                    = isset( $_POST['post_id'][ $index ] ) ? absint( $_POST['post_id'][ $index ] ) : 0;
3801
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3802
				$post_id = 0;
3803
			}
3804
3805
			if ( $update_media_item ) {
3806
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3807
					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...
3808
				}
3809
3810
				$media_array = $_FILES['media'];
3811
3812
				$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...
3813
				$file_array['type']     = $media_array['type'][0];
3814
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3815
				$file_array['error']    = $media_array['error'][0];
3816
				$file_array['size']     = $media_array['size'][0];
3817
3818
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3819
3820
				if ( is_wp_error( $edited_media_item ) ) {
3821
					return $edited_media_item;
3822
				}
3823
3824
				$response = (object) array(
3825
					'id'   => (string) $post_id,
3826
					'file' => (string) $edited_media_item->post_title,
3827
					'url'  => (string) wp_get_attachment_url( $post_id ),
3828
					'type' => (string) $edited_media_item->post_mime_type,
3829
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3830
				);
3831
3832
				return (array) array( $response );
3833
			}
3834
3835
			$attachment_id = media_handle_upload(
3836
				'.jetpack.upload.',
3837
				$post_id,
3838
				array(),
3839
				array(
3840
					'action' => 'jetpack_upload_file',
3841
				)
3842
			);
3843
3844
			if ( ! $attachment_id ) {
3845
				$uploaded_files[ $index ] = (object) array(
3846
					'error'             => 'unknown',
3847
					'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site',
3848
				);
3849
			} elseif ( is_wp_error( $attachment_id ) ) {
3850
				$uploaded_files[ $index ] = (object) array(
3851
					'error'             => 'attachment_' . $attachment_id->get_error_code(),
3852
					'error_description' => $attachment_id->get_error_message(),
3853
				);
3854
			} else {
3855
				$attachment               = get_post( $attachment_id );
3856
				$uploaded_files[ $index ] = (object) array(
3857
					'id'   => (string) $attachment_id,
3858
					'file' => $attachment->post_title,
3859
					'url'  => wp_get_attachment_url( $attachment_id ),
3860
					'type' => $attachment->post_mime_type,
3861
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3862
				);
3863
				// Zip files uploads are not supported unless they are done for installation purposed
3864
				// lets delete them in case something goes wrong in this whole process
3865
				if ( 'application/zip' === $attachment->post_mime_type ) {
3866
					// Schedule a cleanup for 2 hours from now in case of failed install.
3867
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3868
				}
3869
			}
3870
		}
3871
		if ( ! is_null( $global_post ) ) {
3872
			$GLOBALS['post'] = $global_post;
3873
		}
3874
3875
		return $uploaded_files;
3876
	}
3877
3878
	/**
3879
	 * Add help to the Jetpack page
3880
	 *
3881
	 * @since Jetpack (1.2.3)
3882
	 * @return false if not the Jetpack page
3883
	 */
3884
	function admin_help() {
3885
		$current_screen = get_current_screen();
3886
3887
		// Overview
3888
		$current_screen->add_help_tab(
3889
			array(
3890
				'id'      => 'home',
3891
				'title'   => __( 'Home', 'jetpack' ),
3892
				'content' =>
3893
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3894
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3895
					'<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>',
3896
			)
3897
		);
3898
3899
		// Screen Content
3900
		if ( current_user_can( 'manage_options' ) ) {
3901
			$current_screen->add_help_tab(
3902
				array(
3903
					'id'      => 'settings',
3904
					'title'   => __( 'Settings', 'jetpack' ),
3905
					'content' =>
3906
						'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3907
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3908
						'<ol>' .
3909
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3910
							'<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>' .
3911
						'</ol>' .
3912
						'<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>',
3913
				)
3914
			);
3915
		}
3916
3917
		// Help Sidebar
3918
		$support_url = Redirect::get_url( 'jetpack-support' );
3919
		$faq_url     = Redirect::get_url( 'jetpack-faq' );
3920
		$current_screen->set_help_sidebar(
3921
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3922
			'<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3923
			'<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3924
			'<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3925
		);
3926
	}
3927
3928
	function admin_menu_css() {
3929
		wp_enqueue_style( 'jetpack-icons' );
3930
	}
3931
3932
	function admin_menu_order() {
3933
		return true;
3934
	}
3935
3936 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3937
		$jp_menu_order = array();
3938
3939
		foreach ( $menu_order as $index => $item ) {
3940
			if ( $item != 'jetpack' ) {
3941
				$jp_menu_order[] = $item;
3942
			}
3943
3944
			if ( $index == 0 ) {
3945
				$jp_menu_order[] = 'jetpack';
3946
			}
3947
		}
3948
3949
		return $jp_menu_order;
3950
	}
3951
3952
	function admin_banner_styles() {
3953
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3954
3955 View Code Duplication
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3956
			wp_register_style(
3957
				'jetpack-dops-style',
3958
				plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3959
				array(),
3960
				JETPACK__VERSION
3961
			);
3962
		}
3963
3964
		wp_enqueue_style(
3965
			'jetpack',
3966
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3967
			array( 'jetpack-dops-style' ),
3968
			JETPACK__VERSION . '-20121016'
3969
		);
3970
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3971
		wp_style_add_data( 'jetpack', 'suffix', $min );
3972
	}
3973
3974
	function plugin_action_links( $actions ) {
3975
3976
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), 'Jetpack' ) );
3977
3978
		if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_active() || ( new Status() )->is_offline_mode() ) ) {
3979
			return array_merge(
3980
				$jetpack_home,
3981
				array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3982
				array( 'support' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack-debugger ' ), __( 'Support', 'jetpack' ) ) ),
3983
				$actions
3984
			);
3985
		}
3986
3987
		return array_merge( $jetpack_home, $actions );
3988
	}
3989
3990
	/**
3991
	 * Adds the deactivation warning modal if there are other active plugins using the connection
3992
	 *
3993
	 * @param string $hook The current admin page.
3994
	 *
3995
	 * @return void
3996
	 */
3997
	public function deactivate_dialog( $hook ) {
3998
		if (
3999
			'plugins.php' === $hook
4000
			&& self::is_active()
4001
		) {
4002
4003
			$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4004
4005
			if ( count( $active_plugins_using_connection ) > 1 ) {
4006
4007
				add_thickbox();
4008
4009
				wp_register_script(
4010
					'jp-tracks',
4011
					'//stats.wp.com/w.js',
4012
					array(),
4013
					gmdate( 'YW' ),
4014
					true
4015
				);
4016
4017
				wp_register_script(
4018
					'jp-tracks-functions',
4019
					plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ),
4020
					array( 'jp-tracks' ),
4021
					JETPACK__VERSION,
4022
					false
4023
				);
4024
4025
				wp_enqueue_script(
4026
					'jetpack-deactivate-dialog-js',
4027
					Assets::get_file_url_for_environment(
4028
						'_inc/build/jetpack-deactivate-dialog.min.js',
4029
						'_inc/jetpack-deactivate-dialog.js'
4030
					),
4031
					array( 'jquery', 'jp-tracks-functions' ),
4032
					JETPACK__VERSION,
4033
					true
4034
				);
4035
4036
				wp_localize_script(
4037
					'jetpack-deactivate-dialog-js',
4038
					'deactivate_dialog',
4039
					array(
4040
						'title'            => __( 'Deactivate Jetpack', 'jetpack' ),
4041
						'deactivate_label' => __( 'Disconnect and Deactivate', 'jetpack' ),
4042
						'tracksUserData'   => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
4043
					)
4044
				);
4045
4046
				add_action( 'admin_footer', array( $this, 'deactivate_dialog_content' ) );
4047
4048
				wp_enqueue_style( 'jetpack-deactivate-dialog', plugins_url( 'css/jetpack-deactivate-dialog.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
4049
			}
4050
		}
4051
	}
4052
4053
	/**
4054
	 * Outputs the content of the deactivation modal
4055
	 *
4056
	 * @return void
4057
	 */
4058
	public function deactivate_dialog_content() {
4059
		$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4060
		unset( $active_plugins_using_connection['jetpack'] );
4061
		$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 4059 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...
4062
	}
4063
4064
	/**
4065
	 * Filters the login URL to include the registration flow in case the user isn't logged in.
4066
	 *
4067
	 * @param string $login_url The wp-login URL.
4068
	 * @param string $redirect  URL to redirect users after logging in.
4069
	 * @since Jetpack 8.4
4070
	 * @return string
4071
	 */
4072
	public function login_url( $login_url, $redirect ) {
4073
		parse_str( wp_parse_url( $redirect, PHP_URL_QUERY ), $redirect_parts );
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_QUERY.

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...
4074
		if ( ! empty( $redirect_parts[ self::$jetpack_redirect_login ] ) ) {
4075
			$login_url = add_query_arg( self::$jetpack_redirect_login, 'true', $login_url );
4076
		}
4077
		return $login_url;
4078
	}
4079
4080
	/**
4081
	 * Redirects non-authenticated users to authenticate with Calypso if redirect flag is set.
4082
	 *
4083
	 * @since Jetpack 8.4
4084
	 */
4085
	public function login_init() {
4086
		// phpcs:ignore WordPress.Security.NonceVerification
4087
		if ( ! empty( $_GET[ self::$jetpack_redirect_login ] ) ) {
4088
			add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4089
			wp_safe_redirect(
4090
				add_query_arg(
4091
					array(
4092
						'forceInstall' => 1,
4093
						'url'          => rawurlencode( get_site_url() ),
4094
					),
4095
					// @todo provide way to go to specific calypso env.
4096
					self::get_calypso_host() . 'jetpack/connect'
4097
				)
4098
			);
4099
			exit;
4100
		}
4101
	}
4102
4103
	/*
4104
	 * Registration flow:
4105
	 * 1 - ::admin_page_load() action=register
4106
	 * 2 - ::try_registration()
4107
	 * 3 - ::register()
4108
	 *     - Creates jetpack_register option containing two secrets and a timestamp
4109
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4110
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4111
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
4112
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4113
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4114
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4115
	 *       jetpack_id, jetpack_secret, jetpack_public
4116
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4117
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4118
	 * 5 - user logs in with WP.com account
4119
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4120
	 *		- Manager::authorize()
4121
	 *		- Manager::get_token()
4122
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
4123
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4124
	 *			- which responds with access_token, token_type, scope
4125
	 *		- Manager::authorize() stores jetpack_options: user_token => access_token.$user_id
4126
	 *		- Jetpack::activate_default_modules()
4127
	 *     		- Deactivates deprecated plugins
4128
	 *     		- Activates all default modules
4129
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4130
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4131
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4132
	 *     Done!
4133
	 */
4134
4135
	/**
4136
	 * Handles the page load events for the Jetpack admin page
4137
	 */
4138
	function admin_page_load() {
4139
		$error = false;
4140
4141
		// Make sure we have the right body class to hook stylings for subpages off of.
4142
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ), 20 );
4143
4144
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
4145
			// Should only be used in intermediate redirects to preserve state across redirects
4146
			self::restate();
4147
		}
4148
4149
		if ( isset( $_GET['connect_url_redirect'] ) ) {
4150
			// @todo: Add validation against a known allowed list.
4151
			$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4152
			// User clicked in the iframe to link their accounts
4153
			if ( ! self::is_user_connected() ) {
4154
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4155
4156
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4157
				$connect_url = $this->build_connect_url( true, $redirect, $from );
4158
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4159
4160
				if ( isset( $_GET['notes_iframe'] ) ) {
4161
					$connect_url .= '&notes_iframe';
4162
				}
4163
				wp_redirect( $connect_url );
4164
				exit;
4165
			} else {
4166
				if ( ! isset( $_GET['calypso_env'] ) ) {
4167
					self::state( 'message', 'already_authorized' );
4168
					wp_safe_redirect( self::admin_url() );
4169
					exit;
4170
				} else {
4171
					$connect_url  = $this->build_connect_url( true, false, $from );
4172
					$connect_url .= '&already_authorized=true';
4173
					wp_redirect( $connect_url );
4174
					exit;
4175
				}
4176
			}
4177
		}
4178
4179
		if ( isset( $_GET['action'] ) ) {
4180
			switch ( $_GET['action'] ) {
4181
				case 'authorize':
4182
					if ( self::is_active() && self::is_user_connected() ) {
4183
						self::state( 'message', 'already_authorized' );
4184
						wp_safe_redirect( self::admin_url() );
4185
						exit;
4186
					}
4187
					self::log( 'authorize' );
4188
					$client_server = new Jetpack_Client_Server();
4189
					$client_server->client_authorize();
4190
					exit;
4191
				case 'register':
4192
					if ( ! current_user_can( 'jetpack_connect' ) ) {
4193
						$error = 'cheatin';
4194
						break;
4195
					}
4196
					check_admin_referer( 'jetpack-register' );
4197
					self::log( 'register' );
4198
					self::maybe_set_version_option();
4199
					$registered = self::try_registration();
4200 View Code Duplication
					if ( is_wp_error( $registered ) ) {
4201
						$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...
4202
						self::state( 'error', $error );
4203
						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...
4204
4205
						/**
4206
						 * Jetpack registration Error.
4207
						 *
4208
						 * @since 7.5.0
4209
						 *
4210
						 * @param string|int $error The error code.
4211
						 * @param \WP_Error $registered The error object.
4212
						 */
4213
						do_action( 'jetpack_connection_register_fail', $error, $registered );
4214
						break;
4215
					}
4216
4217
					$from     = isset( $_GET['from'] ) ? $_GET['from'] : false;
4218
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4219
4220
					/**
4221
					 * Jetpack registration Success.
4222
					 *
4223
					 * @since 7.5.0
4224
					 *
4225
					 * @param string $from 'from' GET parameter;
4226
					 */
4227
					do_action( 'jetpack_connection_register_success', $from );
4228
4229
					$url = $this->build_connect_url( true, $redirect, $from );
4230
4231
					if ( ! empty( $_GET['onboarding'] ) ) {
4232
						$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4233
					}
4234
4235
					if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4236
						$url = add_query_arg( 'auth_approved', 'true', $url );
4237
					}
4238
4239
					wp_redirect( $url );
4240
					exit;
4241
				case 'activate':
4242
					if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4243
						$error = 'cheatin';
4244
						break;
4245
					}
4246
4247
					$module = stripslashes( $_GET['module'] );
4248
					check_admin_referer( "jetpack_activate-$module" );
4249
					self::log( 'activate', $module );
4250
					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...
4251
						self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4252
					}
4253
					// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4254
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4255
					exit;
4256
				case 'activate_default_modules':
4257
					check_admin_referer( 'activate_default_modules' );
4258
					self::log( 'activate_default_modules' );
4259
					self::restate();
4260
					$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4261
					$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4262
					$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4263
					self::activate_default_modules( $min_version, $max_version, $other_modules );
4264
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4265
					exit;
4266
				case 'disconnect':
4267
					if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4268
						$error = 'cheatin';
4269
						break;
4270
					}
4271
4272
					check_admin_referer( 'jetpack-disconnect' );
4273
					self::log( 'disconnect' );
4274
					self::disconnect();
4275
					wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4276
					exit;
4277
				case 'reconnect':
4278
					if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4279
						$error = 'cheatin';
4280
						break;
4281
					}
4282
4283
					check_admin_referer( 'jetpack-reconnect' );
4284
					self::log( 'reconnect' );
4285
					$this->disconnect();
4286
					wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4287
					exit;
4288 View Code Duplication
				case 'deactivate':
4289
					if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4290
						$error = 'cheatin';
4291
						break;
4292
					}
4293
4294
					$modules = stripslashes( $_GET['module'] );
4295
					check_admin_referer( "jetpack_deactivate-$modules" );
4296
					foreach ( explode( ',', $modules ) as $module ) {
4297
						self::log( 'deactivate', $module );
4298
						self::deactivate_module( $module );
4299
						self::state( 'message', 'module_deactivated' );
4300
					}
4301
					self::state( 'module', $modules );
4302
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4303
					exit;
4304
				case 'unlink':
4305
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4306
					check_admin_referer( 'jetpack-unlink' );
4307
					self::log( 'unlink' );
4308
					Connection_Manager::disconnect_user();
4309
					self::state( 'message', 'unlinked' );
4310
					if ( 'sub-unlink' == $redirect ) {
4311
						wp_safe_redirect( admin_url() );
4312
					} else {
4313
						wp_safe_redirect( self::admin_url( array( 'page' => $redirect ) ) );
4314
					}
4315
					exit;
4316
				case 'onboard':
4317
					if ( ! current_user_can( 'manage_options' ) ) {
4318
						wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4319
					} else {
4320
						self::create_onboarding_token();
4321
						$url = $this->build_connect_url( true );
4322
4323
						if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4324
							$url = add_query_arg( 'onboarding', $token, $url );
4325
						}
4326
4327
						$calypso_env = $this->get_calypso_env();
4328
						if ( ! empty( $calypso_env ) ) {
4329
							$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4330
						}
4331
4332
						wp_redirect( $url );
4333
						exit;
4334
					}
4335
					exit;
4336
				default:
4337
					/**
4338
					 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4339
					 *
4340
					 * @since 2.6.0
4341
					 *
4342
					 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4343
					 */
4344
					do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4345
			}
4346
		}
4347
4348
		if ( ! $error = $error ? $error : self::state( 'error' ) ) {
4349
			self::activate_new_modules( true );
4350
		}
4351
4352
		$message_code = self::state( 'message' );
4353
		if ( self::state( 'optin-manage' ) ) {
4354
			$activated_manage = $message_code;
4355
			$message_code     = 'jetpack-manage';
4356
		}
4357
4358
		switch ( $message_code ) {
4359
			case 'jetpack-manage':
4360
				$sites_url = esc_url( Redirect::get_url( 'calypso-sites' ) );
4361
				// translators: %s is the URL to the "Sites" panel on wordpress.com.
4362
				$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>';
4363
				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...
4364
					$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack' ) . '</strong>';
4365
				}
4366
				break;
4367
4368
		}
4369
4370
		$deactivated_plugins = self::state( 'deactivated_plugins' );
4371
4372
		if ( ! empty( $deactivated_plugins ) ) {
4373
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4374
			$deactivated_titles  = array();
4375
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4376
				if ( ! isset( $this->plugins_to_deactivate[ $deactivated_plugin ] ) ) {
4377
					continue;
4378
				}
4379
4380
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[ $deactivated_plugin ][1] ) . '</strong>';
4381
			}
4382
4383
			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...
4384
				if ( $this->message ) {
4385
					$this->message .= "<br /><br />\n";
4386
				}
4387
4388
				$this->message .= wp_sprintf(
4389
					_n(
4390
						'Jetpack contains the most recent version of the old %l plugin.',
4391
						'Jetpack contains the most recent versions of the old %l plugins.',
4392
						count( $deactivated_titles ),
4393
						'jetpack'
4394
					),
4395
					$deactivated_titles
4396
				);
4397
4398
				$this->message .= "<br />\n";
4399
4400
				$this->message .= _n(
4401
					'The old version has been deactivated and can be removed from your site.',
4402
					'The old versions have been deactivated and can be removed from your site.',
4403
					count( $deactivated_titles ),
4404
					'jetpack'
4405
				);
4406
			}
4407
		}
4408
4409
		$this->privacy_checks = self::state( 'privacy_checks' );
4410
4411
		if ( $this->message || $this->error || $this->privacy_checks ) {
4412
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4413
		}
4414
4415
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4416
	}
4417
4418
	function admin_notices() {
4419
4420
		if ( $this->error ) {
4421
			?>
4422
<div id="message" class="jetpack-message jetpack-err">
4423
	<div class="squeezer">
4424
		<h2>
4425
			<?php
4426
			echo wp_kses(
4427
				$this->error,
4428
				array(
4429
					'a'      => array( 'href' => array() ),
4430
					'small'  => true,
4431
					'code'   => true,
4432
					'strong' => true,
4433
					'br'     => true,
4434
					'b'      => true,
4435
				)
4436
			);
4437
			?>
4438
			</h2>
4439
			<?php	if ( $desc = self::state( 'error_description' ) ) : ?>
4440
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4441
<?php	endif; ?>
4442
	</div>
4443
</div>
4444
			<?php
4445
		}
4446
4447
		if ( $this->message ) {
4448
			?>
4449
<div id="message" class="jetpack-message">
4450
	<div class="squeezer">
4451
		<h2>
4452
			<?php
4453
			echo wp_kses(
4454
				$this->message,
4455
				array(
4456
					'strong' => array(),
4457
					'a'      => array( 'href' => true ),
4458
					'br'     => true,
4459
				)
4460
			);
4461
			?>
4462
			</h2>
4463
	</div>
4464
</div>
4465
			<?php
4466
		}
4467
4468
		if ( $this->privacy_checks ) :
4469
			$module_names = $module_slugs = array();
4470
4471
			$privacy_checks = explode( ',', $this->privacy_checks );
4472
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4473
			foreach ( $privacy_checks as $module_slug ) {
4474
				$module = self::get_module( $module_slug );
4475
				if ( ! $module ) {
4476
					continue;
4477
				}
4478
4479
				$module_slugs[] = $module_slug;
4480
				$module_names[] = "<strong>{$module['name']}</strong>";
4481
			}
4482
4483
			$module_slugs = join( ',', $module_slugs );
4484
			?>
4485
<div id="message" class="jetpack-message jetpack-err">
4486
	<div class="squeezer">
4487
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4488
		<p>
4489
			<?php
4490
			echo wp_kses(
4491
				wptexturize(
4492
					wp_sprintf(
4493
						_nx(
4494
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4495
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4496
							count( $privacy_checks ),
4497
							'%l = list of Jetpack module/feature names',
4498
							'jetpack'
4499
						),
4500
						$module_names
4501
					)
4502
				),
4503
				array( 'strong' => true )
4504
			);
4505
4506
			echo "\n<br />\n";
4507
4508
			echo wp_kses(
4509
				sprintf(
4510
					_nx(
4511
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4512
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4513
						count( $privacy_checks ),
4514
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4515
						'jetpack'
4516
					),
4517
					wp_nonce_url(
4518
						self::admin_url(
4519
							array(
4520
								'page'   => 'jetpack',
4521
								'action' => 'deactivate',
4522
								'module' => urlencode( $module_slugs ),
4523
							)
4524
						),
4525
						"jetpack_deactivate-$module_slugs"
4526
					),
4527
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4528
				),
4529
				array(
4530
					'a' => array(
4531
						'href'  => true,
4532
						'title' => true,
4533
					),
4534
				)
4535
			);
4536
			?>
4537
		</p>
4538
	</div>
4539
</div>
4540
			<?php
4541
endif;
4542
	}
4543
4544
	/**
4545
	 * We can't always respond to a signed XML-RPC request with a
4546
	 * helpful error message. In some circumstances, doing so could
4547
	 * leak information.
4548
	 *
4549
	 * Instead, track that the error occurred via a Jetpack_Option,
4550
	 * and send that data back in the heartbeat.
4551
	 * All this does is increment a number, but it's enough to find
4552
	 * trends.
4553
	 *
4554
	 * @param WP_Error $xmlrpc_error The error produced during
4555
	 *                               signature validation.
4556
	 */
4557
	function track_xmlrpc_error( $xmlrpc_error ) {
4558
		$code = is_wp_error( $xmlrpc_error )
4559
			? $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...
4560
			: 'should-not-happen';
4561
4562
		$xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4563
		if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4564
			// No need to update the option if we already have
4565
			// this code stored.
4566
			return;
4567
		}
4568
		$xmlrpc_errors[ $code ] = true;
4569
4570
		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...
4571
	}
4572
4573
	/**
4574
	 * Initialize the jetpack stats instance only when needed
4575
	 *
4576
	 * @return void
4577
	 */
4578
	private function initialize_stats() {
4579
		if ( is_null( $this->a8c_mc_stats_instance ) ) {
4580
			$this->a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4581
		}
4582
	}
4583
4584
	/**
4585
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4586
	 */
4587
	function stat( $group, $detail ) {
4588
		$this->initialize_stats();
4589
		$this->a8c_mc_stats_instance->add( $group, $detail );
4590
4591
		// Keep a local copy for backward compatibility (there are some direct checks on this).
4592
		$this->stats = $this->a8c_mc_stats_instance->get_current_stats();
4593
	}
4594
4595
	/**
4596
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4597
	 */
4598
	function do_stats( $method = '' ) {
4599
		$this->initialize_stats();
4600
		if ( 'server_side' === $method ) {
4601
			$this->a8c_mc_stats_instance->do_server_side_stats();
4602
		} else {
4603
			$this->a8c_mc_stats_instance->do_stats();
4604
		}
4605
4606
		// Keep a local copy for backward compatibility (there are some direct checks on this).
4607
		$this->stats = array();
4608
	}
4609
4610
	/**
4611
	 * Runs stats code for a one-off, server-side.
4612
	 *
4613
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4614
	 *
4615
	 * @return bool If it worked.
4616
	 */
4617
	static function do_server_side_stat( $args ) {
4618
		$url                   = self::build_stats_url( $args );
4619
		$a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4620
		return $a8c_mc_stats_instance->do_server_side_stat( $url );
4621
	}
4622
4623
	/**
4624
	 * Builds the stats url.
4625
	 *
4626
	 * @param $args array|string The arguments to append to the URL.
4627
	 *
4628
	 * @return string The URL to be pinged.
4629
	 */
4630
	static function build_stats_url( $args ) {
4631
4632
		$a8c_mc_stats_instance = new Automattic\Jetpack\A8c_Mc_Stats();
4633
		return $a8c_mc_stats_instance->build_stats_url( $args );
4634
4635
	}
4636
4637
	/**
4638
	 * Get the role of the current user.
4639
	 *
4640
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_current_user_to_role() instead.
4641
	 *
4642
	 * @access public
4643
	 * @static
4644
	 *
4645
	 * @return string|boolean Current user's role, false if not enough capabilities for any of the roles.
4646
	 */
4647
	public static function translate_current_user_to_role() {
4648
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4649
4650
		$roles = new Roles();
4651
		return $roles->translate_current_user_to_role();
4652
	}
4653
4654
	/**
4655
	 * Get the role of a particular user.
4656
	 *
4657
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_user_to_role() instead.
4658
	 *
4659
	 * @access public
4660
	 * @static
4661
	 *
4662
	 * @param \WP_User $user User object.
4663
	 * @return string|boolean User's role, false if not enough capabilities for any of the roles.
4664
	 */
4665
	public static function translate_user_to_role( $user ) {
4666
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4667
4668
		$roles = new Roles();
4669
		return $roles->translate_user_to_role( $user );
4670
	}
4671
4672
	/**
4673
	 * Get the minimum capability for a role.
4674
	 *
4675
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_role_to_cap() instead.
4676
	 *
4677
	 * @access public
4678
	 * @static
4679
	 *
4680
	 * @param string $role Role name.
4681
	 * @return string|boolean Capability, false if role isn't mapped to any capabilities.
4682
	 */
4683
	public static function translate_role_to_cap( $role ) {
4684
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4685
4686
		$roles = new Roles();
4687
		return $roles->translate_role_to_cap( $role );
4688
	}
4689
4690
	/**
4691
	 * Sign a user role with the master access token.
4692
	 * If not specified, will default to the current user.
4693
	 *
4694
	 * @deprecated since 7.7
4695
	 * @see Automattic\Jetpack\Connection\Manager::sign_role()
4696
	 *
4697
	 * @access public
4698
	 * @static
4699
	 *
4700
	 * @param string $role    User role.
4701
	 * @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...
4702
	 * @return string Signed user role.
4703
	 */
4704
	public static function sign_role( $role, $user_id = null ) {
4705
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::sign_role' );
4706
		return self::connection()->sign_role( $role, $user_id );
4707
	}
4708
4709
	/**
4710
	 * Builds a URL to the Jetpack connection auth page
4711
	 *
4712
	 * @since 3.9.5
4713
	 *
4714
	 * @param bool        $raw If true, URL will not be escaped.
4715
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4716
	 *                              If string, will be a custom redirect.
4717
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4718
	 * @param bool        $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4719
	 *
4720
	 * @return string Connect URL
4721
	 */
4722
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4723
		$site_id    = Jetpack_Options::get_option( 'id' );
4724
		$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...
4725
4726
		if ( $register || ! $blog_token || ! $site_id ) {
4727
			$url = self::nonce_url_no_esc( self::admin_url( 'action=register' ), 'jetpack-register' );
4728
4729
			if ( ! empty( $redirect ) ) {
4730
				$url = add_query_arg(
4731
					'redirect',
4732
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4733
					$url
4734
				);
4735
			}
4736
4737
			if ( is_network_admin() ) {
4738
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4739
			}
4740
4741
			$calypso_env = self::get_calypso_env();
4742
4743
			if ( ! empty( $calypso_env ) ) {
4744
				$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4745
			}
4746
		} else {
4747
4748
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4749
			// because otherwise this logic can get us in to a loop.
4750
			$last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) );
4751
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4752
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4753
4754
				$response = Client::wpcom_json_api_request_as_blog(
4755
					sprintf( '/sites/%d', $site_id ) . '?force=wpcom',
4756
					'1.1'
4757
				);
4758
4759
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4760
4761
					// Generating a register URL instead to refresh the existing token
4762
					return $this->build_connect_url( $raw, $redirect, $from, true );
4763
				}
4764
			}
4765
4766
			$url = $this->build_authorize_url( $redirect );
0 ignored issues
show
It seems like $redirect defined by parameter $redirect on line 4722 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...
4767
		}
4768
4769
		if ( $from ) {
4770
			$url = add_query_arg( 'from', $from, $url );
4771
		}
4772
4773
		$url = $raw ? esc_url_raw( $url ) : esc_url( $url );
4774
		/**
4775
		 * Filter the URL used when connecting a user to a WordPress.com account.
4776
		 *
4777
		 * @since 8.1.0
4778
		 *
4779
		 * @param string $url Connection URL.
4780
		 * @param bool   $raw If true, URL will not be escaped.
4781
		 */
4782
		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...
4783
	}
4784
4785
	public static function build_authorize_url( $redirect = false, $iframe = false ) {
4786
4787
		add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4788
		add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4789
		add_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4790
4791
		if ( $iframe ) {
4792
			add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4793
		}
4794
4795
		$c8n = self::connection();
4796
		$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...
4797
4798
		remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4799
		remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4800
		remove_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4801
4802
		if ( $iframe ) {
4803
			remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4804
		}
4805
4806
		/**
4807
		 * Filter the URL used when authorizing a user to a WordPress.com account.
4808
		 *
4809
		 * @since 8.9.0
4810
		 *
4811
		 * @param string $url Connection URL.
4812
		 */
4813
		return apply_filters( 'jetpack_build_authorize_url', $url );
4814
	}
4815
4816
	/**
4817
	 * Filters the connection URL parameter array.
4818
	 *
4819
	 * @param array $args default URL parameters used by the package.
4820
	 * @return array the modified URL arguments array.
4821
	 */
4822
	public static function filter_connect_request_body( $args ) {
4823
		if (
4824
			Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4825
			&& include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4826
		) {
4827
			$gp_locale      = GP_Locales::by_field( 'wp_locale', get_locale() );
4828
			$args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4829
				? $gp_locale->slug
4830
				: '';
4831
		}
4832
4833
		$tracking        = new Tracking();
4834
		$tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4835
4836
		$args = array_merge(
4837
			$args,
4838
			array(
4839
				'_ui' => $tracks_identity['_ui'],
4840
				'_ut' => $tracks_identity['_ut'],
4841
			)
4842
		);
4843
4844
		$calypso_env = self::get_calypso_env();
4845
4846
		if ( ! empty( $calypso_env ) ) {
4847
			$args['calypso_env'] = $calypso_env;
4848
		}
4849
4850
		return $args;
4851
	}
4852
4853
	/**
4854
	 * Filters the URL that will process the connection data. It can be different from the URL
4855
	 * that we send the user to after everything is done.
4856
	 *
4857
	 * @param String $processing_url the default redirect URL used by the package.
4858
	 * @return String the modified URL.
4859
	 */
4860
	public static function filter_connect_processing_url( $processing_url ) {
4861
		$processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4862
		return $processing_url;
4863
	}
4864
4865
	/**
4866
	 * Filters the redirection URL that is used for connect requests. The redirect
4867
	 * URL should return the user back to the Jetpack console.
4868
	 *
4869
	 * @param String $redirect the default redirect URL used by the package.
4870
	 * @return String the modified URL.
4871
	 */
4872
	public static function filter_connect_redirect_url( $redirect ) {
4873
		$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4874
		$redirect           = $redirect
4875
			? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4876
			: $jetpack_admin_page;
4877
4878
		if ( isset( $_REQUEST['is_multisite'] ) ) {
4879
			$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4880
		}
4881
4882
		return $redirect;
4883
	}
4884
4885
	/**
4886
	 * This action fires at the beginning of the Manager::authorize method.
4887
	 */
4888
	public static function authorize_starting() {
4889
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
4890
		// Checking if site has been active/connected previously before recording unique connection.
4891
		if ( ! $jetpack_unique_connection ) {
4892
			// jetpack_unique_connection option has never been set.
4893
			$jetpack_unique_connection = array(
4894
				'connected'    => 0,
4895
				'disconnected' => 0,
4896
				'version'      => '3.6.1',
4897
			);
4898
4899
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
4900
4901
			// Track unique connection.
4902
			$jetpack = self::init();
4903
4904
			$jetpack->stat( 'connections', 'unique-connection' );
4905
			$jetpack->do_stats( 'server_side' );
4906
		}
4907
4908
		// Increment number of times connected.
4909
		$jetpack_unique_connection['connected'] += 1;
4910
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
4911
	}
4912
4913
	/**
4914
	 * This action fires at the end of the Manager::authorize method when a secondary user is
4915
	 * linked.
4916
	 */
4917
	public static function authorize_ending_linked() {
4918
		// Don't activate anything since we are just connecting a user.
4919
		self::state( 'message', 'linked' );
4920
	}
4921
4922
	/**
4923
	 * This action fires at the end of the Manager::authorize method when the master user is
4924
	 * authorized.
4925
	 *
4926
	 * @param array $data The request data.
4927
	 */
4928
	public static function authorize_ending_authorized( $data ) {
4929
		// If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4930
		self::invalidate_onboarding_token();
4931
4932
		// If redirect_uri is SSO, ensure SSO module is enabled.
4933
		parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_QUERY.

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...
4934
4935
		/** This filter is documented in class.jetpack-cli.php */
4936
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
4937
4938
		$activate_sso = (
4939
			isset( $redirect_options['action'] ) &&
4940
			'jetpack-sso' === $redirect_options['action'] &&
4941
			$jetpack_start_enable_sso
4942
		);
4943
4944
		$do_redirect_on_error = ( 'client' === $data['auth_type'] );
4945
4946
		self::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
4947
	}
4948
4949
	/**
4950
	 * This action fires at the end of the REST_Connector connection_reconnect method when the
4951
	 * reconnect process is completed.
4952
	 * Note that this currently only happens when we don't need the user to re-authorize
4953
	 * their WP.com account, eg in cases where we are restoring a connection with
4954
	 * unhealthy blog token.
4955
	 */
4956
	public static function reconnection_completed() {
4957
		self::state( 'message', 'reconnection_completed' );
4958
	}
4959
4960
	/**
4961
	 * Get our assumed site creation date.
4962
	 * Calculated based on the earlier date of either:
4963
	 * - Earliest admin user registration date.
4964
	 * - Earliest date of post of any post type.
4965
	 *
4966
	 * @since 7.2.0
4967
	 * @deprecated since 7.8.0
4968
	 *
4969
	 * @return string Assumed site creation date and time.
4970
	 */
4971
	public static function get_assumed_site_creation_date() {
4972
		_deprecated_function( __METHOD__, 'jetpack-7.8', 'Automattic\\Jetpack\\Connection\\Manager' );
4973
		return self::connection()->get_assumed_site_creation_date();
4974
	}
4975
4976 View Code Duplication
	public static function apply_activation_source_to_args( &$args ) {
4977
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
4978
4979
		if ( $activation_source_name ) {
4980
			$args['_as'] = urlencode( $activation_source_name );
4981
		}
4982
4983
		if ( $activation_source_keyword ) {
4984
			$args['_ak'] = urlencode( $activation_source_keyword );
4985
		}
4986
	}
4987
4988
	function build_reconnect_url( $raw = false ) {
4989
		$url = wp_nonce_url( self::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4990
		return $raw ? $url : esc_url( $url );
4991
	}
4992
4993
	public static function admin_url( $args = null ) {
4994
		$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...
4995
		$url  = add_query_arg( $args, admin_url( 'admin.php' ) );
4996
		return $url;
4997
	}
4998
4999
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
5000
		$actionurl = str_replace( '&amp;', '&', $actionurl );
5001
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
5002
	}
5003
5004
	function dismiss_jetpack_notice() {
5005
5006
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
5007
			return;
5008
		}
5009
5010
		switch ( $_GET['jetpack-notice'] ) {
5011
			case 'dismiss':
5012
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
5013
5014
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
5015
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
5016
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
5017
				}
5018
				break;
5019
		}
5020
	}
5021
5022
	public static function sort_modules( $a, $b ) {
5023
		if ( $a['sort'] == $b['sort'] ) {
5024
			return 0;
5025
		}
5026
5027
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5028
	}
5029
5030
	function ajax_recheck_ssl() {
5031
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
5032
		$result = self::permit_ssl( true );
5033
		wp_send_json(
5034
			array(
5035
				'enabled' => $result,
5036
				'message' => get_transient( 'jetpack_https_test_message' ),
5037
			)
5038
		);
5039
	}
5040
5041
	/* Client API */
5042
5043
	/**
5044
	 * Returns the requested Jetpack API URL
5045
	 *
5046
	 * @deprecated since 7.7
5047
	 * @return string
5048
	 */
5049
	public static function api_url( $relative_url ) {
5050
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::api_url' );
5051
		$connection = self::connection();
5052
		return $connection->api_url( $relative_url );
5053
	}
5054
5055
	/**
5056
	 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::fix_url_for_bad_hosts() instead.
5057
	 *
5058
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
5059
	 */
5060
	public static function fix_url_for_bad_hosts( $url ) {
5061
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::fix_url_for_bad_hosts' );
5062
		return Connection_Utils::fix_url_for_bad_hosts( $url );
5063
	}
5064
5065
	public static function verify_onboarding_token( $token_data, $token, $request_data ) {
5066
		// Default to a blog token.
5067
		$token_type = 'blog';
5068
5069
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5070
		if ( isset( $request_data ) || ! empty( $_GET['onboarding'] ) ) {
5071
			if ( ! empty( $_GET['onboarding'] ) ) {
5072
				$jpo = $_GET;
5073
			} else {
5074
				$jpo = json_decode( $request_data, true );
5075
			}
5076
5077
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5078
			$jpo_user  = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5079
5080
			if (
5081
				isset( $jpo_user )
5082
				&& isset( $jpo_token )
5083
				&& is_email( $jpo_user )
5084
				&& ctype_alnum( $jpo_token )
5085
				&& isset( $_GET['rest_route'] )
5086
				&& self::validate_onboarding_token_action(
5087
					$jpo_token,
5088
					$_GET['rest_route']
5089
				)
5090
			) {
5091
				$jp_user = get_user_by( 'email', $jpo_user );
5092
				if ( is_a( $jp_user, 'WP_User' ) ) {
5093
					wp_set_current_user( $jp_user->ID );
5094
					$user_can = is_multisite()
5095
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5096
						: current_user_can( 'manage_options' );
5097
					if ( $user_can ) {
5098
						$token_type              = 'user';
5099
						$token->external_user_id = $jp_user->ID;
5100
					}
5101
				}
5102
			}
5103
5104
			$token_data['type']    = $token_type;
5105
			$token_data['user_id'] = $token->external_user_id;
5106
		}
5107
5108
		return $token_data;
5109
	}
5110
5111
	/**
5112
	 * Create a random secret for validating onboarding payload
5113
	 *
5114
	 * @return string Secret token
5115
	 */
5116
	public static function create_onboarding_token() {
5117
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
5118
			$token = wp_generate_password( 32, false );
5119
			Jetpack_Options::update_option( 'onboarding', $token );
5120
		}
5121
5122
		return $token;
5123
	}
5124
5125
	/**
5126
	 * Remove the onboarding token
5127
	 *
5128
	 * @return bool True on success, false on failure
5129
	 */
5130
	public static function invalidate_onboarding_token() {
5131
		return Jetpack_Options::delete_option( 'onboarding' );
5132
	}
5133
5134
	/**
5135
	 * Validate an onboarding token for a specific action
5136
	 *
5137
	 * @return boolean True if token/action pair is accepted, false if not
5138
	 */
5139
	public static function validate_onboarding_token_action( $token, $action ) {
5140
		// Compare tokens, bail if tokens do not match
5141
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5142
			return false;
5143
		}
5144
5145
		// List of valid actions we can take
5146
		$valid_actions = array(
5147
			'/jetpack/v4/settings',
5148
		);
5149
5150
		// Only allow valid actions.
5151
		if ( ! in_array( $action, $valid_actions ) ) {
5152
			return false;
5153
		}
5154
5155
		return true;
5156
	}
5157
5158
	/**
5159
	 * Checks to see if the URL is using SSL to connect with Jetpack
5160
	 *
5161
	 * @since 2.3.3
5162
	 * @return boolean
5163
	 */
5164
	public static function permit_ssl( $force_recheck = false ) {
5165
		// Do some fancy tests to see if ssl is being supported
5166
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5167
			$message = '';
5168
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5169
				$ssl = 0;
5170
			} else {
5171
				switch ( JETPACK_CLIENT__HTTPS ) {
5172
					case 'NEVER':
5173
						$ssl     = 0;
5174
						$message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' );
5175
						break;
5176
					case 'ALWAYS':
5177
					case 'AUTO':
5178
					default:
5179
						$ssl = 1;
5180
						break;
5181
				}
5182
5183
				// If it's not 'NEVER', test to see
5184
				if ( $ssl ) {
5185
					if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5186
						$ssl     = 0;
5187
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
5188
					} else {
5189
						$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5190
						if ( is_wp_error( $response ) ) {
5191
							$ssl     = 0;
5192
							$message = __( 'WordPress reports no SSL support', 'jetpack' );
5193
						} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5194
							$ssl     = 0;
5195
							$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5196
						}
5197
					}
5198
				}
5199
			}
5200
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5201
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5202
		}
5203
5204
		return (bool) $ssl;
5205
	}
5206
5207
	/*
5208
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working.
5209
	 */
5210
	public function alert_auto_ssl_fail() {
5211
		if ( ! current_user_can( 'manage_options' ) ) {
5212
			return;
5213
		}
5214
5215
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5216
		?>
5217
5218
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5219
			<div class="jp-banner__content">
5220
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5221
				<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>
5222
				<p>
5223
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5224
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5225
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5226
				</p>
5227
				<p>
5228
					<?php
5229
					printf(
5230
						__( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
5231
						esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ),
5232
						esc_url( Redirect::get_url( 'jetpack-support-getting-started-troubleshooting-tips' ) )
5233
					);
5234
					?>
5235
				</p>
5236
			</div>
5237
		</div>
5238
		<style>
5239
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5240
		</style>
5241
		<script type="text/javascript">
5242
			jQuery( document ).ready( function( $ ) {
5243
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5244
					var $this = $( this );
5245
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5246
					$( '#jetpack-recheck-ssl-output' ).html( '' );
5247
					e.preventDefault();
5248
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5249
					$.post( ajaxurl, data )
5250
					  .done( function( response ) {
5251
						  if ( response.enabled ) {
5252
							  $( '#jetpack-ssl-warning' ).hide();
5253
						  } else {
5254
							  this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5255
							  $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5256
						  }
5257
					  }.bind( $this ) );
5258
				} );
5259
			} );
5260
		</script>
5261
5262
		<?php
5263
	}
5264
5265
	/**
5266
	 * Returns the Jetpack XML-RPC API
5267
	 *
5268
	 * @deprecated 8.0 Use Connection_Manager instead.
5269
	 * @return string
5270
	 */
5271
	public static function xmlrpc_api_url() {
5272
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_api_url()' );
5273
		return self::connection()->xmlrpc_api_url();
5274
	}
5275
5276
	/**
5277
	 * Returns the connection manager object.
5278
	 *
5279
	 * @return Automattic\Jetpack\Connection\Manager
5280
	 */
5281
	public static function connection() {
5282
		$jetpack = static::init();
5283
5284
		// If the connection manager hasn't been instantiated, do that now.
5285
		if ( ! $jetpack->connection_manager ) {
5286
			$jetpack->connection_manager = new Connection_Manager( 'jetpack' );
5287
		}
5288
5289
		return $jetpack->connection_manager;
5290
	}
5291
5292
	/**
5293
	 * Creates two secret tokens and the end of life timestamp for them.
5294
	 *
5295
	 * Note these tokens are unique per call, NOT static per site for connecting.
5296
	 *
5297
	 * @since 2.6
5298
	 * @param String  $action  The action name.
5299
	 * @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...
5300
	 * @param Integer $exp     Expiration time in seconds.
5301
	 * @return array
5302
	 */
5303
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5304
		return self::connection()->generate_secrets( $action, $user_id, $exp );
5305
	}
5306
5307
	public static function get_secrets( $action, $user_id ) {
5308
		$secrets = self::connection()->get_secrets( $action, $user_id );
5309
5310
		if ( Connection_Manager::SECRETS_MISSING === $secrets ) {
5311
			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...
5312
		}
5313
5314
		if ( Connection_Manager::SECRETS_EXPIRED === $secrets ) {
5315
			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...
5316
		}
5317
5318
		return $secrets;
5319
	}
5320
5321
	/**
5322
	 * @deprecated 7.5 Use Connection_Manager instead.
5323
	 *
5324
	 * @param $action
5325
	 * @param $user_id
5326
	 */
5327
	public static function delete_secrets( $action, $user_id ) {
5328
		return self::connection()->delete_secrets( $action, $user_id );
5329
	}
5330
5331
	/**
5332
	 * Builds the timeout limit for queries talking with the wpcom servers.
5333
	 *
5334
	 * Based on local php max_execution_time in php.ini
5335
	 *
5336
	 * @since 2.6
5337
	 * @return int
5338
	 * @deprecated
5339
	 **/
5340
	public function get_remote_query_timeout_limit() {
5341
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
5342
		return self::get_max_execution_time();
5343
	}
5344
5345
	/**
5346
	 * Builds the timeout limit for queries talking with the wpcom servers.
5347
	 *
5348
	 * Based on local php max_execution_time in php.ini
5349
	 *
5350
	 * @since 5.4
5351
	 * @return int
5352
	 **/
5353
	public static function get_max_execution_time() {
5354
		$timeout = (int) ini_get( 'max_execution_time' );
5355
5356
		// Ensure exec time set in php.ini
5357
		if ( ! $timeout ) {
5358
			$timeout = 30;
5359
		}
5360
		return $timeout;
5361
	}
5362
5363
	/**
5364
	 * Sets a minimum request timeout, and returns the current timeout
5365
	 *
5366
	 * @since 5.4
5367
	 **/
5368 View Code Duplication
	public static function set_min_time_limit( $min_timeout ) {
5369
		$timeout = self::get_max_execution_time();
5370
		if ( $timeout < $min_timeout ) {
5371
			$timeout = $min_timeout;
5372
			set_time_limit( $timeout );
5373
		}
5374
		return $timeout;
5375
	}
5376
5377
	/**
5378
	 * Takes the response from the Jetpack register new site endpoint and
5379
	 * verifies it worked properly.
5380
	 *
5381
	 * @since 2.6
5382
	 * @deprecated since 7.7.0
5383
	 * @see Automattic\Jetpack\Connection\Manager::validate_remote_register_response()
5384
	 **/
5385
	public function validate_remote_register_response() {
5386
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::validate_remote_register_response' );
5387
	}
5388
5389
	/**
5390
	 * @return bool|WP_Error
5391
	 */
5392
	public static function register() {
5393
		$tracking = new Tracking();
5394
		$tracking->record_user_event( 'jpc_register_begin' );
5395
5396
		add_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5397
5398
		$connection   = self::connection();
5399
		$registration = $connection->register();
5400
5401
		remove_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5402
5403
		if ( ! $registration || is_wp_error( $registration ) ) {
5404
			return $registration;
5405
		}
5406
5407
		return true;
5408
	}
5409
5410
	/**
5411
	 * Filters the registration request body to include tracking properties.
5412
	 *
5413
	 * @param array $properties
5414
	 * @return array amended properties.
5415
	 */
5416 View Code Duplication
	public static function filter_register_request_body( $properties ) {
5417
		$tracking        = new Tracking();
5418
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5419
5420
		return array_merge(
5421
			$properties,
5422
			array(
5423
				'_ui' => $tracks_identity['_ui'],
5424
				'_ut' => $tracks_identity['_ut'],
5425
			)
5426
		);
5427
	}
5428
5429
	/**
5430
	 * Filters the token request body to include tracking properties.
5431
	 *
5432
	 * @param array $properties
5433
	 * @return array amended properties.
5434
	 */
5435 View Code Duplication
	public static function filter_token_request_body( $properties ) {
5436
		$tracking        = new Tracking();
5437
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5438
5439
		return array_merge(
5440
			$properties,
5441
			array(
5442
				'_ui' => $tracks_identity['_ui'],
5443
				'_ut' => $tracks_identity['_ut'],
5444
			)
5445
		);
5446
	}
5447
5448
	/**
5449
	 * If the db version is showing something other that what we've got now, bump it to current.
5450
	 *
5451
	 * @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...
5452
	 */
5453
	public static function maybe_set_version_option() {
5454
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5455
		if ( JETPACK__VERSION != $version ) {
5456
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5457
5458
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5459
				/** This action is documented in class.jetpack.php */
5460
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5461
			}
5462
5463
			return true;
5464
		}
5465
		return false;
5466
	}
5467
5468
	/* Client Server API */
5469
5470
	/**
5471
	 * Loads the Jetpack XML-RPC client.
5472
	 * No longer necessary, as the XML-RPC client will be automagically loaded.
5473
	 *
5474
	 * @deprecated since 7.7.0
5475
	 */
5476
	public static function load_xml_rpc_client() {
5477
		_deprecated_function( __METHOD__, 'jetpack-7.7' );
5478
	}
5479
5480
	/**
5481
	 * Resets the saved authentication state in between testing requests.
5482
	 *
5483
	 * @deprecated since 8.9.0
5484
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::reset_saved_auth_state()
5485
	 */
5486
	public function reset_saved_auth_state() {
5487
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::reset_saved_auth_state' );
5488
		Connection_Rest_Authentication::init()->reset_saved_auth_state();
5489
	}
5490
5491
	/**
5492
	 * Verifies the signature of the current request.
5493
	 *
5494
	 * @deprecated since 7.7.0
5495
	 * @see Automattic\Jetpack\Connection\Manager::verify_xml_rpc_signature()
5496
	 *
5497
	 * @return false|array
5498
	 */
5499
	public function verify_xml_rpc_signature() {
5500
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::verify_xml_rpc_signature' );
5501
		return self::connection()->verify_xml_rpc_signature();
5502
	}
5503
5504
	/**
5505
	 * Verifies the signature of the current request.
5506
	 *
5507
	 * This function has side effects and should not be used. Instead,
5508
	 * use the memoized version `->verify_xml_rpc_signature()`.
5509
	 *
5510
	 * @deprecated since 7.7.0
5511
	 * @see Automattic\Jetpack\Connection\Manager::internal_verify_xml_rpc_signature()
5512
	 * @internal
5513
	 */
5514
	private function internal_verify_xml_rpc_signature() {
5515
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::internal_verify_xml_rpc_signature' );
5516
	}
5517
5518
	/**
5519
	 * Authenticates XML-RPC and other requests from the Jetpack Server.
5520
	 *
5521
	 * @deprecated since 7.7.0
5522
	 * @see Automattic\Jetpack\Connection\Manager::authenticate_jetpack()
5523
	 *
5524
	 * @param \WP_User|mixed $user     User object if authenticated.
5525
	 * @param string         $username Username.
5526
	 * @param string         $password Password string.
5527
	 * @return \WP_User|mixed Authenticated user or error.
5528
	 */
5529 View Code Duplication
	public function authenticate_jetpack( $user, $username, $password ) {
5530
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::authenticate_jetpack' );
5531
5532
		if ( ! $this->connection_manager ) {
5533
			$this->connection_manager = new Connection_Manager();
5534
		}
5535
5536
		return $this->connection_manager->authenticate_jetpack( $user, $username, $password );
5537
	}
5538
5539
	/**
5540
	 * Authenticates requests from Jetpack server to WP REST API endpoints.
5541
	 * Uses the existing XMLRPC request signing implementation.
5542
	 *
5543
	 * @deprecated since 8.9.0
5544
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authenticate()
5545
	 */
5546
	function wp_rest_authenticate( $user ) {
5547
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenticate' );
5548
		return Connection_Rest_Authentication::init()->wp_rest_authenticate( $user );
5549
	}
5550
5551
	/**
5552
	 * Report authentication status to the WP REST API.
5553
	 *
5554
	 * @deprecated since 8.9.0
5555
	 * @see Automattic\Jetpack\Connection\Rest_Authentication::wp_rest_authentication_errors()
5556
	 *
5557
	 * @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...
5558
	 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5559
	 */
5560
	public function wp_rest_authentication_errors( $value ) {
5561
		_deprecated_function( __METHOD__, 'jetpack-8.9', 'Automattic\\Jetpack\\Connection\\Rest_Authentication::wp_rest_authenication_errors' );
5562
		return Connection_Rest_Authentication::init()->wp_rest_authentication_errors( $value );
5563
	}
5564
5565
	/**
5566
	 * Add our nonce to this request.
5567
	 *
5568
	 * @deprecated since 7.7.0
5569
	 * @see Automattic\Jetpack\Connection\Manager::add_nonce()
5570
	 *
5571
	 * @param int    $timestamp Timestamp of the request.
5572
	 * @param string $nonce     Nonce string.
5573
	 */
5574 View Code Duplication
	public function add_nonce( $timestamp, $nonce ) {
5575
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::add_nonce' );
5576
5577
		if ( ! $this->connection_manager ) {
5578
			$this->connection_manager = new Connection_Manager();
5579
		}
5580
5581
		return $this->connection_manager->add_nonce( $timestamp, $nonce );
5582
	}
5583
5584
	/**
5585
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5586
	 * Capture it here so we can verify the signature later.
5587
	 *
5588
	 * @deprecated since 7.7.0
5589
	 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_methods()
5590
	 *
5591
	 * @param array $methods XMLRPC methods.
5592
	 * @return array XMLRPC methods, with the $HTTP_RAW_POST_DATA one.
5593
	 */
5594 View Code Duplication
	public function xmlrpc_methods( $methods ) {
5595
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_methods' );
5596
5597
		if ( ! $this->connection_manager ) {
5598
			$this->connection_manager = new Connection_Manager();
5599
		}
5600
5601
		return $this->connection_manager->xmlrpc_methods( $methods );
5602
	}
5603
5604
	/**
5605
	 * Register additional public XMLRPC methods.
5606
	 *
5607
	 * @deprecated since 7.7.0
5608
	 * @see Automattic\Jetpack\Connection\Manager::public_xmlrpc_methods()
5609
	 *
5610
	 * @param array $methods Public XMLRPC methods.
5611
	 * @return array Public XMLRPC methods, with the getOptions one.
5612
	 */
5613 View Code Duplication
	public function public_xmlrpc_methods( $methods ) {
5614
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::public_xmlrpc_methods' );
5615
5616
		if ( ! $this->connection_manager ) {
5617
			$this->connection_manager = new Connection_Manager();
5618
		}
5619
5620
		return $this->connection_manager->public_xmlrpc_methods( $methods );
5621
	}
5622
5623
	/**
5624
	 * Handles a getOptions XMLRPC method call.
5625
	 *
5626
	 * @deprecated since 7.7.0
5627
	 * @see Automattic\Jetpack\Connection\Manager::jetpack_getOptions()
5628
	 *
5629
	 * @param array $args method call arguments.
5630
	 * @return array an amended XMLRPC server options array.
5631
	 */
5632 View Code Duplication
	public function jetpack_getOptions( $args ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
5633
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::jetpack_getOptions' );
5634
5635
		if ( ! $this->connection_manager ) {
5636
			$this->connection_manager = new Connection_Manager();
5637
		}
5638
5639
		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...
5640
	}
5641
5642
	/**
5643
	 * Adds Jetpack-specific options to the output of the XMLRPC options method.
5644
	 *
5645
	 * @deprecated since 7.7.0
5646
	 * @see Automattic\Jetpack\Connection\Manager::xmlrpc_options()
5647
	 *
5648
	 * @param array $options Standard Core options.
5649
	 * @return array Amended options.
5650
	 */
5651 View Code Duplication
	public function xmlrpc_options( $options ) {
5652
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_options' );
5653
5654
		if ( ! $this->connection_manager ) {
5655
			$this->connection_manager = new Connection_Manager();
5656
		}
5657
5658
		return $this->connection_manager->xmlrpc_options( $options );
5659
	}
5660
5661
	/**
5662
	 * Cleans nonces that were saved when calling ::add_nonce.
5663
	 *
5664
	 * @deprecated since 7.7.0
5665
	 * @see Automattic\Jetpack\Connection\Manager::clean_nonces()
5666
	 *
5667
	 * @param bool $all whether to clean even non-expired nonces.
5668
	 */
5669
	public static function clean_nonces( $all = false ) {
5670
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::clean_nonces' );
5671
		return self::connection()->clean_nonces( $all );
5672
	}
5673
5674
	/**
5675
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5676
	 * SET: state( $key, $value );
5677
	 * GET: $value = state( $key );
5678
	 *
5679
	 * @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...
5680
	 * @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...
5681
	 * @param bool   $restate private
5682
	 */
5683
	public static function state( $key = null, $value = null, $restate = false ) {
5684
		static $state = array();
5685
		static $path, $domain;
5686
		if ( ! isset( $path ) ) {
5687
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
5688
			$admin_url = self::admin_url();
5689
			$bits      = wp_parse_url( $admin_url );
5690
5691
			if ( is_array( $bits ) ) {
5692
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5693
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5694
			} else {
5695
				$path = $domain = null;
5696
			}
5697
		}
5698
5699
		// Extract state from cookies and delete cookies
5700
		if ( isset( $_COOKIE['jetpackState'] ) && is_array( $_COOKIE['jetpackState'] ) ) {
5701
			$yum = wp_unslash( $_COOKIE['jetpackState'] );
5702
			unset( $_COOKIE['jetpackState'] );
5703
			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...
5704
				if ( strlen( $v ) ) {
5705
					$state[ $k ] = $v;
5706
				}
5707
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5708
			}
5709
		}
5710
5711
		if ( $restate ) {
5712
			foreach ( $state as $k => $v ) {
5713
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5714
			}
5715
			return;
5716
		}
5717
5718
		// Get a state variable.
5719
		if ( isset( $key ) && ! isset( $value ) ) {
5720
			if ( array_key_exists( $key, $state ) ) {
5721
				return $state[ $key ];
5722
			}
5723
			return null;
5724
		}
5725
5726
		// Set a state variable.
5727
		if ( isset( $key ) && isset( $value ) ) {
5728
			if ( is_array( $value ) && isset( $value[0] ) ) {
5729
				$value = $value[0];
5730
			}
5731
			$state[ $key ] = $value;
5732
			if ( ! headers_sent() ) {
5733
				if ( self::should_set_cookie( $key ) ) {
5734
					setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5735
				}
5736
			}
5737
		}
5738
	}
5739
5740
	public static function restate() {
5741
		self::state( null, null, true );
5742
	}
5743
5744
	/**
5745
	 * Determines whether the jetpackState[$key] value should be added to the
5746
	 * cookie.
5747
	 *
5748
	 * @param string $key The state key.
5749
	 *
5750
	 * @return boolean Whether the value should be added to the cookie.
5751
	 */
5752
	public static function should_set_cookie( $key ) {
5753
		global $current_screen;
5754
		$page = isset( $current_screen->base ) ? $current_screen->base : null;
5755
5756
		if ( 'toplevel_page_jetpack' === $page && 'display_update_modal' === $key ) {
5757
			return false;
5758
		}
5759
5760
		return true;
5761
	}
5762
5763
	public static function check_privacy( $file ) {
5764
		static $is_site_publicly_accessible = null;
5765
5766
		if ( is_null( $is_site_publicly_accessible ) ) {
5767
			$is_site_publicly_accessible = false;
5768
5769
			$rpc = new Jetpack_IXR_Client();
5770
5771
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5772
			if ( $success ) {
5773
				$response = $rpc->getResponse();
5774
				if ( $response ) {
5775
					$is_site_publicly_accessible = true;
5776
				}
5777
			}
5778
5779
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5780
		}
5781
5782
		if ( $is_site_publicly_accessible ) {
5783
			return;
5784
		}
5785
5786
		$module_slug = self::get_module_slug( $file );
5787
5788
		$privacy_checks = self::state( 'privacy_checks' );
5789
		if ( ! $privacy_checks ) {
5790
			$privacy_checks = $module_slug;
5791
		} else {
5792
			$privacy_checks .= ",$module_slug";
5793
		}
5794
5795
		self::state( 'privacy_checks', $privacy_checks );
5796
	}
5797
5798
	/**
5799
	 * Helper method for multicall XMLRPC.
5800
	 *
5801
	 * @deprecated since 8.9.0
5802
	 * @see Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call()
5803
	 *
5804
	 * @param ...$args Args for the async_call.
5805
	 */
5806
	public static function xmlrpc_async_call( ...$args ) {
5807
5808
		_deprecated_function( 'Jetpack::xmlrpc_async_call', 'jetpack-8.9.0', 'Automattic\\Jetpack\\Connection\\Xmlrpc_Async_Call::add_call' );
5809
5810
		global $blog_id;
5811
		static $clients = array();
5812
5813
		$client_blog_id = is_multisite() ? $blog_id : 0;
5814
5815
		if ( ! isset( $clients[ $client_blog_id ] ) ) {
5816
			$clients[ $client_blog_id ] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER ) );
5817
			if ( function_exists( 'ignore_user_abort' ) ) {
5818
				ignore_user_abort( true );
5819
			}
5820
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5821
		}
5822
5823
		if ( ! empty( $args[0] ) ) {
5824
			call_user_func_array( array( $clients[ $client_blog_id ], 'addCall' ), $args );
5825
		} elseif ( is_multisite() ) {
5826
			foreach ( $clients as $client_blog_id => $client ) {
5827
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5828
					continue;
5829
				}
5830
5831
				$switch_success = switch_to_blog( $client_blog_id, true );
5832
				if ( ! $switch_success ) {
5833
					continue;
5834
				}
5835
5836
				flush();
5837
				$client->query();
5838
5839
				restore_current_blog();
5840
			}
5841
		} else {
5842
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5843
				flush();
5844
				$clients[0]->query();
5845
			}
5846
		}
5847
	}
5848
5849
	public static function staticize_subdomain( $url ) {
5850
5851
		// Extract hostname from URL
5852
		$host = wp_parse_url( $url, PHP_URL_HOST );
0 ignored issues
show
The call to wp_parse_url() has too many arguments starting with PHP_URL_HOST.

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...
5853
5854
		// Explode hostname on '.'
5855
		$exploded_host = explode( '.', $host );
5856
5857
		// Retrieve the name and TLD
5858
		if ( count( $exploded_host ) > 1 ) {
5859
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
5860
			$tld  = $exploded_host[ count( $exploded_host ) - 1 ];
5861
			// Rebuild domain excluding subdomains
5862
			$domain = $name . '.' . $tld;
5863
		} else {
5864
			$domain = $host;
5865
		}
5866
		// Array of Automattic domains.
5867
		$domains_allowed = array( 'wordpress.com', 'wp.com' );
5868
5869
		// Return $url if not an Automattic domain.
5870
		if ( ! in_array( $domain, $domains_allowed, true ) ) {
5871
			return $url;
5872
		}
5873
5874
		if ( is_ssl() ) {
5875
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
5876
		}
5877
5878
		srand( crc32( basename( $url ) ) );
5879
		$static_counter = rand( 0, 2 );
5880
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
5881
5882
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
5883
	}
5884
5885
	/* JSON API Authorization */
5886
5887
	/**
5888
	 * Handles the login action for Authorizing the JSON API
5889
	 */
5890
	function login_form_json_api_authorization() {
5891
		$this->verify_json_api_authorization_request();
5892
5893
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
5894
5895
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
5896
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5897
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
5898
	}
5899
5900
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
5901
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
5902
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
5903
			return $url;
5904
		}
5905
5906
		$parsed_url = wp_parse_url( $url );
5907
		$url        = strtok( $url, '?' );
5908
		$url        = "$url?{$_SERVER['QUERY_STRING']}";
5909
		if ( ! empty( $parsed_url['query'] ) ) {
5910
			$url .= "&{$parsed_url['query']}";
5911
		}
5912
5913
		return $url;
5914
	}
5915
5916
	// Make sure the POSTed request is handled by the same action
5917
	function preserve_action_in_login_form_for_json_api_authorization() {
5918
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
5919
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
5920
	}
5921
5922
	// If someone logs in to approve API access, store the Access Code in usermeta
5923
	function store_json_api_authorization_token( $user_login, $user ) {
5924
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5925
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
5926
		$token = wp_generate_password( 32, false );
5927
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5928
	}
5929
5930
	// Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access.
5931
	function allow_wpcom_public_api_domain( $domains ) {
5932
		$domains[] = 'public-api.wordpress.com';
5933
		return $domains;
5934
	}
5935
5936
	static function is_redirect_encoded( $redirect_url ) {
5937
		return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5938
	}
5939
5940
	// Add all wordpress.com environments to the safe redirect allowed list.
5941
	function allow_wpcom_environments( $domains ) {
5942
		$domains[] = 'wordpress.com';
5943
		$domains[] = 'wpcalypso.wordpress.com';
5944
		$domains[] = 'horizon.wordpress.com';
5945
		$domains[] = 'calypso.localhost';
5946
		return $domains;
5947
	}
5948
5949
	// Add the Access Code details to the public-api.wordpress.com redirect
5950
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5951
		return add_query_arg(
5952
			urlencode_deep(
5953
				array(
5954
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5955
					'jetpack-user-id' => (int) $user->ID,
5956
					'jetpack-state'   => $this->json_api_authorization_request['state'],
5957
				)
5958
			),
5959
			$redirect_to
5960
		);
5961
	}
5962
5963
5964
	/**
5965
	 * Verifies the request by checking the signature
5966
	 *
5967
	 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5968
	 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5969
	 *
5970
	 * @param null|array $environment
5971
	 */
5972
	function verify_json_api_authorization_request( $environment = null ) {
5973
		$environment = is_null( $environment )
5974
			? $_REQUEST
5975
			: $environment;
5976
5977
		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...
5978
		$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...
5979
		if ( ! $token || empty( $token->secret ) ) {
5980
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack' ) );
5981
		}
5982
5983
		$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' );
5984
5985
		// Host has encoded the request URL, probably as a result of a bad http => https redirect
5986
		if ( self::is_redirect_encoded( $_GET['redirect_to'] ) ) {
5987
			/**
5988
			 * Jetpack authorisation request Error.
5989
			 *
5990
			 * @since 7.5.0
5991
			 */
5992
			do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5993
			$die_error = sprintf(
5994
				/* translators: %s is a URL */
5995
				__( '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' ),
5996
				Redirect::get_url( 'jetpack-support-double-encoding' )
5997
			);
5998
		}
5999
6000
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
6001
6002
		if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
6003
			$signature = $jetpack_signature->sign_request(
6004
				$environment['token'],
6005
				$environment['timestamp'],
6006
				$environment['nonce'],
6007
				'',
6008
				'GET',
6009
				$environment['jetpack_json_api_original_query'],
6010
				null,
6011
				true
6012
			);
6013
		} else {
6014
			$signature = $jetpack_signature->sign_current_request(
6015
				array(
6016
					'body'   => null,
6017
					'method' => 'GET',
6018
				)
6019
			);
6020
		}
6021
6022
		if ( ! $signature ) {
6023
			wp_die( $die_error );
6024
		} elseif ( is_wp_error( $signature ) ) {
6025
			wp_die( $die_error );
6026
		} elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
6027
			if ( is_ssl() ) {
6028
				// 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
6029
				$signature = $jetpack_signature->sign_current_request(
6030
					array(
6031
						'scheme' => 'http',
6032
						'body'   => null,
6033
						'method' => 'GET',
6034
					)
6035
				);
6036
				if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
6037
					wp_die( $die_error );
6038
				}
6039
			} else {
6040
				wp_die( $die_error );
6041
			}
6042
		}
6043
6044
		$timestamp = (int) $environment['timestamp'];
6045
		$nonce     = stripslashes( (string) $environment['nonce'] );
6046
6047
		if ( ! $this->connection_manager ) {
6048
			$this->connection_manager = new Connection_Manager();
6049
		}
6050
6051
		if ( ! $this->connection_manager->add_nonce( $timestamp, $nonce ) ) {
6052
			// De-nonce the nonce, at least for 5 minutes.
6053
			// 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)
6054
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
6055
			if ( $old_nonce_time < time() - 300 ) {
6056
				wp_die( __( 'The authorization process expired.  Please go back and try again.', 'jetpack' ) );
6057
			}
6058
		}
6059
6060
		$data         = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
6061
		$data_filters = array(
6062
			'state'        => 'opaque',
6063
			'client_id'    => 'int',
6064
			'client_title' => 'string',
6065
			'client_image' => 'url',
6066
		);
6067
6068
		foreach ( $data_filters as $key => $sanitation ) {
6069
			if ( ! isset( $data->$key ) ) {
6070
				wp_die( $die_error );
6071
			}
6072
6073
			switch ( $sanitation ) {
6074
				case 'int':
6075
					$this->json_api_authorization_request[ $key ] = (int) $data->$key;
6076
					break;
6077
				case 'opaque':
6078
					$this->json_api_authorization_request[ $key ] = (string) $data->$key;
6079
					break;
6080
				case 'string':
6081
					$this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
6082
					break;
6083
				case 'url':
6084
					$this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
6085
					break;
6086
			}
6087
		}
6088
6089
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
6090
			wp_die( $die_error );
6091
		}
6092
	}
6093
6094
	function login_message_json_api_authorization( $message ) {
6095
		return '<p class="message">' . sprintf(
6096
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.', 'jetpack' ),
6097
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
6098
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
6099
	}
6100
6101
	/**
6102
	 * Get $content_width, but with a <s>twist</s> filter.
6103
	 */
6104
	public static function get_content_width() {
6105
		$content_width = ( isset( $GLOBALS['content_width'] ) && is_numeric( $GLOBALS['content_width'] ) )
6106
			? $GLOBALS['content_width']
6107
			: false;
6108
		/**
6109
		 * Filter the Content Width value.
6110
		 *
6111
		 * @since 2.2.3
6112
		 *
6113
		 * @param string $content_width Content Width value.
6114
		 */
6115
		return apply_filters( 'jetpack_content_width', $content_width );
6116
	}
6117
6118
	/**
6119
	 * Pings the WordPress.com Mirror Site for the specified options.
6120
	 *
6121
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6122
	 *
6123
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6124
	 */
6125
	public function get_cloud_site_options( $option_names ) {
6126
		$option_names = array_filter( (array) $option_names, 'is_string' );
6127
6128
		$xml = new Jetpack_IXR_Client();
6129
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
6130
		if ( $xml->isError() ) {
6131
			return array(
6132
				'error_code' => $xml->getErrorCode(),
6133
				'error_msg'  => $xml->getErrorMessage(),
6134
			);
6135
		}
6136
		$cloud_site_options = $xml->getResponse();
6137
6138
		return $cloud_site_options;
6139
	}
6140
6141
	/**
6142
	 * Checks if the site is currently in an identity crisis.
6143
	 *
6144
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6145
	 */
6146
	public static function check_identity_crisis() {
6147
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() || ! self::validate_sync_error_idc_option() ) {
6148
			return false;
6149
		}
6150
6151
		return Jetpack_Options::get_option( 'sync_error_idc' );
6152
	}
6153
6154
	/**
6155
	 * Checks whether the home and siteurl specifically are allowed.
6156
	 * Written so that we don't have re-check $key and $value params every time
6157
	 * we want to check if this site is allowed, for example in footer.php
6158
	 *
6159
	 * @since  3.8.0
6160
	 * @return bool True = already allowed False = not on the allowed list.
6161
	 */
6162
	public static function is_staging_site() {
6163
		_deprecated_function( 'Jetpack::is_staging_site', 'jetpack-8.1', '/Automattic/Jetpack/Status->is_staging_site' );
6164
		return ( new Status() )->is_staging_site();
6165
	}
6166
6167
	/**
6168
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6169
	 *
6170
	 * @since 4.4.0
6171
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6172
	 *
6173
	 * @return bool
6174
	 */
6175
	public static function validate_sync_error_idc_option() {
6176
		$is_valid = false;
6177
6178
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
6179
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
6180
		if ( $sync_error && self::sync_idc_optin() ) {
6181
			$local_options = self::get_sync_error_idc_option();
6182
			// Ensure all values are set.
6183
			if ( isset( $sync_error['home'] ) && isset( $local_options['home'] ) && isset( $sync_error['siteurl'] ) && isset( $local_options['siteurl'] ) ) {
6184
				if ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
6185
					$is_valid = true;
6186
				}
6187
			}
6188
		}
6189
6190
		/**
6191
		 * Filters whether the sync_error_idc option is valid.
6192
		 *
6193
		 * @since 4.4.0
6194
		 *
6195
		 * @param bool $is_valid If the sync_error_idc is valid or not.
6196
		 */
6197
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
6198
6199
		if ( ! $is_valid && $sync_error ) {
6200
			// Since the option exists, and did not validate, delete it
6201
			Jetpack_Options::delete_option( 'sync_error_idc' );
6202
		}
6203
6204
		return $is_valid;
6205
	}
6206
6207
	/**
6208
	 * Normalizes a url by doing three things:
6209
	 *  - Strips protocol
6210
	 *  - Strips www
6211
	 *  - Adds a trailing slash
6212
	 *
6213
	 * @since 4.4.0
6214
	 * @param string $url
6215
	 * @return WP_Error|string
6216
	 */
6217
	public static function normalize_url_protocol_agnostic( $url ) {
6218
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6219 View Code Duplication
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
6220
			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...
6221
		}
6222
6223
		// Strip www and protocols
6224
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6225
		return $url;
6226
	}
6227
6228
	/**
6229
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6230
	 *
6231
	 * @since 4.4.0
6232
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6233
	 *
6234
	 * @param array $response
6235
	 * @return array Array of the local urls, wpcom urls, and error code
6236
	 */
6237
	public static function get_sync_error_idc_option( $response = array() ) {
6238
		// Since the local options will hit the database directly, store the values
6239
		// in a transient to allow for autoloading and caching on subsequent views.
6240
		$local_options = get_transient( 'jetpack_idc_local' );
6241
		if ( false === $local_options ) {
6242
			$local_options = array(
6243
				'home'    => Functions::home_url(),
6244
				'siteurl' => Functions::site_url(),
6245
			);
6246
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
6247
		}
6248
6249
		$options = array_merge( $local_options, $response );
6250
6251
		$returned_values = array();
6252
		foreach ( $options as $key => $option ) {
6253
			if ( 'error_code' === $key ) {
6254
				$returned_values[ $key ] = $option;
6255
				continue;
6256
			}
6257
6258
			if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) {
6259
				continue;
6260
			}
6261
6262
			$returned_values[ $key ] = $normalized_url;
6263
		}
6264
6265
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
6266
6267
		return $returned_values;
6268
	}
6269
6270
	/**
6271
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6272
	 * If set to true, the site will be put into staging mode.
6273
	 *
6274
	 * @since 4.3.2
6275
	 * @return bool
6276
	 */
6277
	public static function sync_idc_optin() {
6278
		if ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
6279
			$default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
6280
		} else {
6281
			$default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
6282
		}
6283
6284
		/**
6285
		 * Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
6286
		 * URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
6287
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
6288
		 *
6289
		 * @since 4.3.2
6290
		 *
6291
		 * @param bool $default Whether the site is opted in to IDC mitigation.
6292
		 */
6293
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
6294
	}
6295
6296
	/**
6297
	 * Maybe Use a .min.css stylesheet, maybe not.
6298
	 *
6299
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6300
	 */
6301
	public static function maybe_min_asset( $url, $path, $plugin ) {
6302
		// Short out on things trying to find actual paths.
6303
		if ( ! $path || empty( $plugin ) ) {
6304
			return $url;
6305
		}
6306
6307
		$path = ltrim( $path, '/' );
6308
6309
		// Strip out the abspath.
6310
		$base = dirname( plugin_basename( $plugin ) );
6311
6312
		// Short out on non-Jetpack assets.
6313
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6314
			return $url;
6315
		}
6316
6317
		// File name parsing.
6318
		$file              = "{$base}/{$path}";
6319
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6320
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6321
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6322
		$extension         = array_shift( $file_name_parts_r );
6323
6324
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6325
			// Already pointing at the minified version.
6326
			if ( 'min' === $file_name_parts_r[0] ) {
6327
				return $url;
6328
			}
6329
6330
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6331
			if ( file_exists( $min_full_path ) ) {
6332
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6333
				// If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6334
				if ( 'css' === $extension ) {
6335
					$key                      = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6336
					self::$min_assets[ $key ] = $path;
6337
				}
6338
			}
6339
		}
6340
6341
		return $url;
6342
	}
6343
6344
	/**
6345
	 * If the asset is minified, let's flag .min as the suffix.
6346
	 *
6347
	 * Attached to `style_loader_src` filter.
6348
	 *
6349
	 * @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...
6350
	 * @param string $handle The registered handle of the script in question.
6351
	 * @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...
6352
	 */
6353
	public static function set_suffix_on_min( $src, $handle ) {
6354
		if ( false === strpos( $src, '.min.css' ) ) {
6355
			return $src;
6356
		}
6357
6358
		if ( ! empty( self::$min_assets ) ) {
6359
			foreach ( self::$min_assets as $file => $path ) {
6360
				if ( false !== strpos( $src, $file ) ) {
6361
					wp_style_add_data( $handle, 'suffix', '.min' );
6362
					return $src;
6363
				}
6364
			}
6365
		}
6366
6367
		return $src;
6368
	}
6369
6370
	/**
6371
	 * Maybe inlines a stylesheet.
6372
	 *
6373
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6374
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6375
	 *
6376
	 * Attached to `style_loader_tag` filter.
6377
	 *
6378
	 * @param string $tag The tag that would link to the external asset.
6379
	 * @param string $handle The registered handle of the script in question.
6380
	 *
6381
	 * @return string
6382
	 */
6383
	public static function maybe_inline_style( $tag, $handle ) {
6384
		global $wp_styles;
6385
		$item = $wp_styles->registered[ $handle ];
6386
6387
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6388
			return $tag;
6389
		}
6390
6391
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6392
			$href = $matches[1];
6393
			// Strip off query string
6394
			if ( $pos = strpos( $href, '?' ) ) {
6395
				$href = substr( $href, 0, $pos );
6396
			}
6397
			// Strip off fragment
6398
			if ( $pos = strpos( $href, '#' ) ) {
6399
				$href = substr( $href, 0, $pos );
6400
			}
6401
		} else {
6402
			return $tag;
6403
		}
6404
6405
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6406
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6407
			return $tag;
6408
		}
6409
6410
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6411
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6412
			// And this isn't the pass that actually deals with the RTL version...
6413
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6414
				// Short out, as the RTL version will deal with it in a moment.
6415
				return $tag;
6416
			}
6417
		}
6418
6419
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6420
		$css  = self::absolutize_css_urls( file_get_contents( $file ), $href );
6421
		if ( $css ) {
6422
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6423
			if ( empty( $item->extra['after'] ) ) {
6424
				wp_add_inline_style( $handle, $css );
6425
			} else {
6426
				array_unshift( $item->extra['after'], $css );
6427
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6428
			}
6429
		}
6430
6431
		return $tag;
6432
	}
6433
6434
	/**
6435
	 * Loads a view file from the views
6436
	 *
6437
	 * Data passed in with the $data parameter will be available in the
6438
	 * template file as $data['value']
6439
	 *
6440
	 * @param string $template - Template file to load
6441
	 * @param array  $data - Any data to pass along to the template
6442
	 * @return boolean - If template file was found
6443
	 **/
6444
	public function load_view( $template, $data = array() ) {
6445
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6446
6447
		if ( file_exists( $views_dir . $template ) ) {
6448
			require_once $views_dir . $template;
6449
			return true;
6450
		}
6451
6452
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6453
		return false;
6454
	}
6455
6456
	/**
6457
	 * Throws warnings for deprecated hooks to be removed from Jetpack that cannot remain in the original place in the code.
6458
	 *
6459
	 * @todo Convert these to use apply_filters_deprecated and do_action_deprecated and remove custom code.
6460
	 */
6461
	public function deprecated_hooks() {
6462
		global $wp_filter;
6463
6464
		/*
6465
		 * Format:
6466
		 * deprecated_filter_name => replacement_name
6467
		 *
6468
		 * If there is no replacement, use null for replacement_name
6469
		 */
6470
		$deprecated_list = array(
6471
			'jetpack_bail_on_shortcode'                    => 'jetpack_shortcodes_to_include',
6472
			'wpl_sharing_2014_1'                           => null,
6473
			'jetpack-tools-to-include'                     => 'jetpack_tools_to_include',
6474
			'jetpack_identity_crisis_options_to_check'     => null,
6475
			'update_option_jetpack_single_user_site'       => null,
6476
			'audio_player_default_colors'                  => null,
6477
			'add_option_jetpack_featured_images_enabled'   => null,
6478
			'add_option_jetpack_update_details'            => null,
6479
			'add_option_jetpack_updates'                   => null,
6480
			'add_option_jetpack_network_name'              => null,
6481
			'add_option_jetpack_network_allow_new_registrations' => null,
6482
			'add_option_jetpack_network_add_new_users'     => null,
6483
			'add_option_jetpack_network_site_upload_space' => null,
6484
			'add_option_jetpack_network_upload_file_types' => null,
6485
			'add_option_jetpack_network_enable_administration_menus' => null,
6486
			'add_option_jetpack_is_multi_site'             => null,
6487
			'add_option_jetpack_is_main_network'           => null,
6488
			'add_option_jetpack_main_network_site'         => null,
6489
			'jetpack_sync_all_registered_options'          => null,
6490
			'jetpack_has_identity_crisis'                  => 'jetpack_sync_error_idc_validation',
6491
			'jetpack_is_post_mailable'                     => null,
6492
			'jetpack_seo_site_host'                        => null,
6493
			'jetpack_installed_plugin'                     => 'jetpack_plugin_installed',
6494
			'jetpack_holiday_snow_option_name'             => null,
6495
			'jetpack_holiday_chance_of_snow'               => null,
6496
			'jetpack_holiday_snow_js_url'                  => null,
6497
			'jetpack_is_holiday_snow_season'               => null,
6498
			'jetpack_holiday_snow_option_updated'          => null,
6499
			'jetpack_holiday_snowing'                      => null,
6500
			'jetpack_sso_auth_cookie_expirtation'          => 'jetpack_sso_auth_cookie_expiration',
6501
			'jetpack_cache_plans'                          => null,
6502
			'jetpack_updated_theme'                        => 'jetpack_updated_themes',
6503
			'jetpack_lazy_images_skip_image_with_atttributes' => 'jetpack_lazy_images_skip_image_with_attributes',
6504
			'jetpack_enable_site_verification'             => null,
6505
			// Removed in Jetpack 7.3.0
6506
			'jetpack_widget_authors_exclude'               => 'jetpack_widget_authors_params',
6507
			// Removed in Jetpack 7.9.0
6508
			'jetpack_pwa_manifest'                         => null,
6509
			'jetpack_pwa_background_color'                 => null,
6510
			// Removed in Jetpack 8.3.0.
6511
			'jetpack_check_mobile'                         => null,
6512
			'jetpack_mobile_stylesheet'                    => null,
6513
			'jetpack_mobile_template'                      => null,
6514
			'mobile_reject_mobile'                         => null,
6515
			'mobile_force_mobile'                          => null,
6516
			'mobile_app_promo_download'                    => null,
6517
			'mobile_setup'                                 => null,
6518
			'jetpack_mobile_footer_before'                 => null,
6519
			'wp_mobile_theme_footer'                       => null,
6520
			'minileven_credits'                            => null,
6521
			'jetpack_mobile_header_before'                 => null,
6522
			'jetpack_mobile_header_after'                  => null,
6523
			'jetpack_mobile_theme_menu'                    => null,
6524
			'minileven_show_featured_images'               => null,
6525
			'minileven_attachment_size'                    => null,
6526
		);
6527
6528
		// This is a silly loop depth. Better way?
6529
		foreach ( $deprecated_list as $hook => $hook_alt ) {
6530
			if ( has_action( $hook ) ) {
6531
				foreach ( $wp_filter[ $hook ] as $func => $values ) {
6532
					foreach ( $values as $hooked ) {
6533
						if ( is_callable( $hooked['function'] ) ) {
6534
							$function_name = $hooked['function'];
6535
						} else {
6536
							$function_name = 'an anonymous function';
6537
						}
6538
						_deprecated_function( $hook . ' used for ' . $function_name, null, $hook_alt );
6539
					}
6540
				}
6541
			}
6542
		}
6543
6544
		$filter_deprecated_list = array(
6545
			'can_display_jetpack_manage_notice' => array(
6546
				'replacement' => null,
6547
				'version'     => 'jetpack-7.3.0',
6548
			),
6549
			'atd_http_post_timeout'             => array(
6550
				'replacement' => null,
6551
				'version'     => 'jetpack-7.3.0',
6552
			),
6553
			'atd_service_domain'                => array(
6554
				'replacement' => null,
6555
				'version'     => 'jetpack-7.3.0',
6556
			),
6557
			'atd_load_scripts'                  => array(
6558
				'replacement' => null,
6559
				'version'     => 'jetpack-7.3.0',
6560
			),
6561
		);
6562
6563
		foreach ( $filter_deprecated_list as $tag => $args ) {
6564
			if ( has_filter( $tag ) ) {
6565
				apply_filters_deprecated( $tag, array(), $args['version'], $args['replacement'] );
6566
			}
6567
		}
6568
6569
		$action_deprecated_list = array(
6570
			'atd_http_post_error' => array(
6571
				'replacement' => null,
6572
				'version'     => 'jetpack-7.3.0',
6573
			),
6574
		);
6575
6576
		foreach ( $action_deprecated_list as $tag => $args ) {
6577
			if ( has_action( $tag ) ) {
6578
				do_action_deprecated( $tag, array(), $args['version'], $args['replacement'] );
6579
			}
6580
		}
6581
	}
6582
6583
	/**
6584
	 * Converts any url in a stylesheet, to the correct absolute url.
6585
	 *
6586
	 * Considerations:
6587
	 *  - Normal, relative URLs     `feh.png`
6588
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6589
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6590
	 *  - Absolute URLs             `http://domain.com/feh.png`
6591
	 *  - Domain root relative URLs `/feh.png`
6592
	 *
6593
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6594
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6595
	 *
6596
	 * @return mixed|string
6597
	 */
6598
	public static function absolutize_css_urls( $css, $css_file_url ) {
6599
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6600
		$css_dir = dirname( $css_file_url );
6601
		$p       = wp_parse_url( $css_dir );
6602
		$domain  = sprintf(
6603
			'%1$s//%2$s%3$s%4$s',
6604
			isset( $p['scheme'] ) ? "{$p['scheme']}:" : '',
6605
			isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6606
			$p['host'],
6607
			isset( $p['port'] ) ? ":{$p['port']}" : ''
6608
		);
6609
6610
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6611
			$find = $replace = array();
6612
			foreach ( $matches as $match ) {
6613
				$url = trim( $match['path'], "'\" \t" );
6614
6615
				// If this is a data url, we don't want to mess with it.
6616
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6617
					continue;
6618
				}
6619
6620
				// If this is an absolute or protocol-agnostic url,
6621
				// we don't want to mess with it.
6622
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6623
					continue;
6624
				}
6625
6626
				switch ( substr( $url, 0, 1 ) ) {
6627
					case '/':
6628
						$absolute = $domain . $url;
6629
						break;
6630
					default:
6631
						$absolute = $css_dir . '/' . $url;
6632
				}
6633
6634
				$find[]    = $match[0];
6635
				$replace[] = sprintf( 'url("%s")', $absolute );
6636
			}
6637
			$css = str_replace( $find, $replace, $css );
6638
		}
6639
6640
		return $css;
6641
	}
6642
6643
	/**
6644
	 * This methods removes all of the registered css files on the front end
6645
	 * from Jetpack in favor of using a single file. In effect "imploding"
6646
	 * all the files into one file.
6647
	 *
6648
	 * Pros:
6649
	 * - Uses only ONE css asset connection instead of 15
6650
	 * - Saves a minimum of 56k
6651
	 * - Reduces server load
6652
	 * - Reduces time to first painted byte
6653
	 *
6654
	 * Cons:
6655
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6656
	 *      should not cause any issues with themes.
6657
	 * - Plugins/themes dequeuing styles no longer do anything. See
6658
	 *      jetpack_implode_frontend_css filter for a workaround
6659
	 *
6660
	 * For some situations developers may wish to disable css imploding and
6661
	 * instead operate in legacy mode where each file loads seperately and
6662
	 * can be edited individually or dequeued. This can be accomplished with
6663
	 * the following line:
6664
	 *
6665
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6666
	 *
6667
	 * @since 3.2
6668
	 **/
6669
	public function implode_frontend_css( $travis_test = false ) {
6670
		$do_implode = true;
6671
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6672
			$do_implode = false;
6673
		}
6674
6675
		// Do not implode CSS when the page loads via the AMP plugin.
6676
		if ( Jetpack_AMP_Support::is_amp_request() ) {
6677
			$do_implode = false;
6678
		}
6679
6680
		/**
6681
		 * Allow CSS to be concatenated into a single jetpack.css file.
6682
		 *
6683
		 * @since 3.2.0
6684
		 *
6685
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6686
		 */
6687
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6688
6689
		// Do not use the imploded file when default behavior was altered through the filter
6690
		if ( ! $do_implode ) {
6691
			return;
6692
		}
6693
6694
		// We do not want to use the imploded file in dev mode, or if not connected
6695
		if ( ( new Status() )->is_offline_mode() || ! self::is_active() ) {
6696
			if ( ! $travis_test ) {
6697
				return;
6698
			}
6699
		}
6700
6701
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6702
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6703
			return;
6704
		}
6705
6706
		/*
6707
		 * Now we assume Jetpack is connected and able to serve the single
6708
		 * file.
6709
		 *
6710
		 * In the future there will be a check here to serve the file locally
6711
		 * or potentially from the Jetpack CDN
6712
		 *
6713
		 * For now:
6714
		 * - Enqueue a single imploded css file
6715
		 * - Zero out the style_loader_tag for the bundled ones
6716
		 * - Be happy, drink scotch
6717
		 */
6718
6719
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6720
6721
		$version = self::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6722
6723
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6724
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6725
	}
6726
6727
	function concat_remove_style_loader_tag( $tag, $handle ) {
6728
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6729
			$tag = '';
6730
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6731
				$tag = '<!-- `' . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6732
			}
6733
		}
6734
6735
		return $tag;
6736
	}
6737
6738
	/**
6739
	 * @deprecated
6740
	 * @see Automattic\Jetpack\Assets\add_aync_script
6741
	 */
6742
	public function script_add_async( $tag, $handle, $src ) {
6743
		_deprecated_function( __METHOD__, 'jetpack-8.6.0' );
6744
	}
6745
6746
	/*
6747
	 * Check the heartbeat data
6748
	 *
6749
	 * Organizes the heartbeat data by severity.  For example, if the site
6750
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6751
	 *
6752
	 * Data will be added to "caution" array, if it either:
6753
	 *  - Out of date Jetpack version
6754
	 *  - Out of date WP version
6755
	 *  - Out of date PHP version
6756
	 *
6757
	 * $return array $filtered_data
6758
	 */
6759
	public static function jetpack_check_heartbeat_data() {
6760
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6761
6762
		$good    = array();
6763
		$caution = array();
6764
		$bad     = array();
6765
6766
		foreach ( $raw_data as $stat => $value ) {
6767
6768
			// Check jetpack version
6769
			if ( 'version' == $stat ) {
6770
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6771
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__VERSION;
6772
					continue;
6773
				}
6774
			}
6775
6776
			// Check WP version
6777
			if ( 'wp-version' == $stat ) {
6778
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6779
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_WP_VERSION;
6780
					continue;
6781
				}
6782
			}
6783
6784
			// Check PHP version
6785
			if ( 'php-version' == $stat ) {
6786
				if ( version_compare( PHP_VERSION, JETPACK__MINIMUM_PHP_VERSION, '<' ) ) {
6787
					$caution[ $stat ] = $value . ' - min supported is ' . JETPACK__MINIMUM_PHP_VERSION;
6788
					continue;
6789
				}
6790
			}
6791
6792
			// Check ID crisis
6793
			if ( 'identitycrisis' == $stat ) {
6794
				if ( 'yes' == $value ) {
6795
					$bad[ $stat ] = $value;
6796
					continue;
6797
				}
6798
			}
6799
6800
			// The rest are good :)
6801
			$good[ $stat ] = $value;
6802
		}
6803
6804
		$filtered_data = array(
6805
			'good'    => $good,
6806
			'caution' => $caution,
6807
			'bad'     => $bad,
6808
		);
6809
6810
		return $filtered_data;
6811
	}
6812
6813
6814
	/*
6815
	 * This method is used to organize all options that can be reset
6816
	 * without disconnecting Jetpack.
6817
	 *
6818
	 * It is used in class.jetpack-cli.php to reset options
6819
	 *
6820
	 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
6821
	 *
6822
	 * @return array of options to delete.
6823
	 */
6824
	public static function get_jetpack_options_for_reset() {
6825
		return Jetpack_Options::get_options_for_reset();
6826
	}
6827
6828
	/*
6829
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6830
	 * so we can bring them directly to their site in calypso.
6831
	 *
6832
	 * @param string | url
6833
	 * @return string | url without the guff
6834
	 */
6835 View Code Duplication
	public static function build_raw_urls( $url ) {
6836
		$strip_http = '/.*?:\/\//i';
6837
		$url        = preg_replace( $strip_http, '', $url );
6838
		$url        = str_replace( '/', '::', $url );
6839
		return $url;
6840
	}
6841
6842
	/**
6843
	 * Stores and prints out domains to prefetch for page speed optimization.
6844
	 *
6845
	 * @deprecated 8.8.0 Use Jetpack::add_resource_hints.
6846
	 *
6847
	 * @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...
6848
	 */
6849
	public static function dns_prefetch( $urls = null ) {
6850
		_deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Automattic\Jetpack\Assets::add_resource_hint' );
6851
		if ( $urls ) {
6852
			Assets::add_resource_hint( $urls );
6853
		}
6854
	}
6855
6856
	public function wp_dashboard_setup() {
6857
		if ( self::is_active() ) {
6858
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6859
		}
6860
6861
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6862
			$jetpack_logo = new Jetpack_Logo();
6863
			$widget_title = sprintf(
6864
				wp_kses(
6865
					/* translators: Placeholder is a Jetpack logo. */
6866
					__( 'Stats <span>by %s</span>', 'jetpack' ),
6867
					array( 'span' => array() )
6868
				),
6869
				$jetpack_logo->get_jp_emblem( true )
6870
			);
6871
6872
			wp_add_dashboard_widget(
6873
				'jetpack_summary_widget',
6874
				$widget_title,
6875
				array( __CLASS__, 'dashboard_widget' )
6876
			);
6877
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6878
			wp_style_add_data( 'jetpack-dashboard-widget', 'rtl', 'replace' );
6879
6880
			// If we're inactive and not in offline mode, sort our box to the top.
6881
			if ( ! self::is_active() && ! ( new Status() )->is_offline_mode() ) {
6882
				global $wp_meta_boxes;
6883
6884
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6885
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6886
6887
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6888
			}
6889
		}
6890
	}
6891
6892
	/**
6893
	 * @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...
6894
	 * @return mixed
6895
	 */
6896
	function get_user_option_meta_box_order_dashboard( $sorted ) {
6897
		if ( ! is_array( $sorted ) ) {
6898
			return $sorted;
6899
		}
6900
6901
		foreach ( $sorted as $box_context => $ids ) {
6902
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6903
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6904
				continue;
6905
			}
6906
6907
			$ids_array = explode( ',', $ids );
6908
			$key       = array_search( 'dashboard_stats', $ids_array );
6909
6910
			if ( false !== $key ) {
6911
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
6912
				$ids_array[ $key ]      = 'jetpack_summary_widget';
6913
				$sorted[ $box_context ] = implode( ',', $ids_array );
6914
				// We've found it, stop searching, and just return.
6915
				break;
6916
			}
6917
		}
6918
6919
		return $sorted;
6920
	}
6921
6922
	public static function dashboard_widget() {
6923
		/**
6924
		 * Fires when the dashboard is loaded.
6925
		 *
6926
		 * @since 3.4.0
6927
		 */
6928
		do_action( 'jetpack_dashboard_widget' );
6929
	}
6930
6931
	public static function dashboard_widget_footer() {
6932
		?>
6933
		<footer>
6934
6935
		<div class="protect">
6936
			<h3><?php esc_html_e( 'Brute force attack protection', 'jetpack' ); ?></h3>
6937
			<?php if ( self::is_module_active( 'protect' ) ) : ?>
6938
				<p class="blocked-count">
6939
					<?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?>
6940
				</p>
6941
				<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>
6942
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! ( new Status() )->is_offline_mode() ) : ?>
6943
				<a href="
6944
				<?php
6945
				echo esc_url(
6946
					wp_nonce_url(
6947
						self::admin_url(
6948
							array(
6949
								'action' => 'activate',
6950
								'module' => 'protect',
6951
							)
6952
						),
6953
						'jetpack_activate-protect'
6954
					)
6955
				);
6956
				?>
6957
							" class="button button-jetpack" title="<?php esc_attr_e( 'Protect helps to keep you secure from brute-force login attacks.', 'jetpack' ); ?>">
6958
					<?php esc_html_e( 'Activate brute force attack protection', 'jetpack' ); ?>
6959
				</a>
6960
			<?php else : ?>
6961
				<?php esc_html_e( 'Brute force attack protection is inactive.', 'jetpack' ); ?>
6962
			<?php endif; ?>
6963
		</div>
6964
6965
		<div class="akismet">
6966
			<h3><?php esc_html_e( 'Anti-spam', 'jetpack' ); ?></h3>
6967
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
6968
				<p class="blocked-count">
6969
					<?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?>
6970
				</p>
6971
				<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>
6972
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
6973
				<a href="
6974
				<?php
6975
				echo esc_url(
6976
					wp_nonce_url(
6977
						add_query_arg(
6978
							array(
6979
								'action' => 'activate',
6980
								'plugin' => 'akismet/akismet.php',
6981
							),
6982
							admin_url( 'plugins.php' )
6983
						),
6984
						'activate-plugin_akismet/akismet.php'
6985
					)
6986
				);
6987
				?>
6988
							" class="button button-jetpack">
6989
					<?php esc_html_e( 'Activate Anti-spam', 'jetpack' ); ?>
6990
				</a>
6991
			<?php else : ?>
6992
				<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>
6993
			<?php endif; ?>
6994
		</div>
6995
6996
		</footer>
6997
		<?php
6998
	}
6999
7000
	/*
7001
	 * Adds a "blank" column in the user admin table to display indication of user connection.
7002
	 */
7003
	function jetpack_icon_user_connected( $columns ) {
7004
		$columns['user_jetpack'] = '';
7005
		return $columns;
7006
	}
7007
7008
	/*
7009
	 * Show Jetpack icon if the user is linked.
7010
	 */
7011
	function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
7012
		if ( 'user_jetpack' == $col && self::is_user_connected( $user_id ) ) {
7013
			$jetpack_logo = new Jetpack_Logo();
7014
			$emblem_html  = sprintf(
7015
				'<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
7016
				esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
7017
				$jetpack_logo->get_jp_emblem()
7018
			);
7019
			return $emblem_html;
7020
		}
7021
7022
		return $val;
7023
	}
7024
7025
	/*
7026
	 * Style the Jetpack user column
7027
	 */
7028
	function jetpack_user_col_style() {
7029
		global $current_screen;
7030
		if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) {
7031
			?>
7032
			<style>
7033
				.fixed .column-user_jetpack {
7034
					width: 21px;
7035
				}
7036
				.jp-emblem-user-admin svg {
7037
					width: 20px;
7038
					height: 20px;
7039
				}
7040
				.jp-emblem-user-admin path {
7041
					fill: #00BE28;
7042
				}
7043
			</style>
7044
			<?php
7045
		}
7046
	}
7047
7048
	/**
7049
	 * Checks if Akismet is active and working.
7050
	 *
7051
	 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
7052
	 * that implied usage of methods present since more recent version.
7053
	 * See https://github.com/Automattic/jetpack/pull/9585
7054
	 *
7055
	 * @since  5.1.0
7056
	 *
7057
	 * @return bool True = Akismet available. False = Aksimet not available.
7058
	 */
7059
	public static function is_akismet_active() {
7060
		static $status = null;
7061
7062
		if ( ! is_null( $status ) ) {
7063
			return $status;
7064
		}
7065
7066
		// Check if a modern version of Akismet is active.
7067
		if ( ! method_exists( 'Akismet', 'http_post' ) ) {
7068
			$status = false;
7069
			return $status;
7070
		}
7071
7072
		// Make sure there is a key known to Akismet at all before verifying key.
7073
		$akismet_key = Akismet::get_api_key();
7074
		if ( ! $akismet_key ) {
7075
			$status = false;
7076
			return $status;
7077
		}
7078
7079
		// Possible values: valid, invalid, failure via Akismet. false if no status is cached.
7080
		$akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
7081
7082
		// 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.
7083
		$recheck = ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state;
7084
		// We cache the result of the Akismet key verification for ten minutes.
7085
		if ( ! $akismet_key_state || $recheck ) {
7086
			$akismet_key_state = Akismet::verify_key( $akismet_key );
7087
			set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
7088
		}
7089
7090
		$status = 'valid' === $akismet_key_state;
7091
7092
		return $status;
7093
	}
7094
7095
	/**
7096
	 * @deprecated
7097
	 *
7098
	 * @see Automattic\Jetpack\Sync\Modules\Users::is_function_in_backtrace
7099
	 */
7100
	public static function is_function_in_backtrace() {
7101
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
7102
	}
7103
7104
	/**
7105
	 * Given a minified path, and a non-minified path, will return
7106
	 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
7107
	 *
7108
	 * Both `$min_base` and `$non_min_base` are expected to be relative to the
7109
	 * root Jetpack directory.
7110
	 *
7111
	 * @since 5.6.0
7112
	 *
7113
	 * @param string $min_path
7114
	 * @param string $non_min_path
7115
	 * @return string The URL to the file
7116
	 */
7117
	public static function get_file_url_for_environment( $min_path, $non_min_path ) {
7118
		return Assets::get_file_url_for_environment( $min_path, $non_min_path );
7119
	}
7120
7121
	/**
7122
	 * Checks for whether Jetpack Backup is enabled.
7123
	 * Will return true if the state of Backup is anything except "unavailable".
7124
	 *
7125
	 * @return bool|int|mixed
7126
	 */
7127
	public static function is_rewind_enabled() {
7128
		if ( ! self::is_active() ) {
7129
			return false;
7130
		}
7131
7132
		$rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7133
		if ( false === $rewind_enabled ) {
7134
			jetpack_require_lib( 'class.core-rest-api-endpoints' );
7135
			$rewind_data    = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7136
			$rewind_enabled = ( ! is_wp_error( $rewind_data )
7137
				&& ! empty( $rewind_data['state'] )
7138
				&& 'active' === $rewind_data['state'] )
7139
				? 1
7140
				: 0;
7141
7142
			set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7143
		}
7144
		return $rewind_enabled;
7145
	}
7146
7147
	/**
7148
	 * Return Calypso environment value; used for developing Jetpack and pairing
7149
	 * it with different Calypso enrionments, such as localhost.
7150
	 *
7151
	 * @since 7.4.0
7152
	 *
7153
	 * @return string Calypso environment
7154
	 */
7155
	public static function get_calypso_env() {
7156
		if ( isset( $_GET['calypso_env'] ) ) {
7157
			return sanitize_key( $_GET['calypso_env'] );
7158
		}
7159
7160
		if ( getenv( 'CALYPSO_ENV' ) ) {
7161
			return sanitize_key( getenv( 'CALYPSO_ENV' ) );
7162
		}
7163
7164
		if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
7165
			return sanitize_key( CALYPSO_ENV );
7166
		}
7167
7168
		return '';
7169
	}
7170
7171
	/**
7172
	 * Returns the hostname with protocol for Calypso.
7173
	 * Used for developing Jetpack with Calypso.
7174
	 *
7175
	 * @since 8.4.0
7176
	 *
7177
	 * @return string Calypso host.
7178
	 */
7179
	public static function get_calypso_host() {
7180
		$calypso_env = self::get_calypso_env();
7181
		switch ( $calypso_env ) {
7182
			case 'development':
7183
				return 'http://calypso.localhost:3000/';
7184
			case 'wpcalypso':
7185
				return 'https://wpcalypso.wordpress.com/';
7186
			case 'horizon':
7187
				return 'https://horizon.wordpress.com/';
7188
			default:
7189
				return 'https://wordpress.com/';
7190
		}
7191
	}
7192
7193
	/**
7194
	 * Handles activating default modules as well general cleanup for the new connection.
7195
	 *
7196
	 * @param boolean $activate_sso                 Whether to activate the SSO module when activating default modules.
7197
	 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7198
	 * @param boolean $send_state_messages          Whether to send state messages.
7199
	 * @return void
7200
	 */
7201
	public static function handle_post_authorization_actions(
7202
		$activate_sso = false,
7203
		$redirect_on_activation_error = false,
7204
		$send_state_messages = true
7205
	) {
7206
		$other_modules = $activate_sso
7207
			? array( 'sso' )
7208
			: array();
7209
7210
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
7211
			self::delete_active_modules();
7212
7213
			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...
7214
		} else {
7215
			self::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages );
7216
		}
7217
7218
		// Since this is a fresh connection, be sure to clear out IDC options
7219
		Jetpack_IDC::clear_all_idc_options();
7220
7221
		if ( $send_state_messages ) {
7222
			self::state( 'message', 'authorized' );
7223
		}
7224
	}
7225
7226
	/**
7227
	 * Returns a boolean for whether backups UI should be displayed or not.
7228
	 *
7229
	 * @return bool Should backups UI be displayed?
7230
	 */
7231
	public static function show_backups_ui() {
7232
		/**
7233
		 * Whether UI for backups should be displayed.
7234
		 *
7235
		 * @since 6.5.0
7236
		 *
7237
		 * @param bool $show_backups Should UI for backups be displayed? True by default.
7238
		 */
7239
		return self::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7240
	}
7241
7242
	/*
7243
	 * Deprecated manage functions
7244
	 */
7245
	function prepare_manage_jetpack_notice() {
7246
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7247
	}
7248
	function manage_activate_screen() {
7249
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7250
	}
7251
	function admin_jetpack_manage_notice() {
7252
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7253
	}
7254
	function opt_out_jetpack_manage_url() {
7255
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7256
	}
7257
	function opt_in_jetpack_manage_url() {
7258
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7259
	}
7260
	function opt_in_jetpack_manage_notice() {
7261
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7262
	}
7263
	function can_display_jetpack_manage_notice() {
7264
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7265
	}
7266
7267
	/**
7268
	 * Clean leftoveruser meta.
7269
	 *
7270
	 * Delete Jetpack-related user meta when it is no longer needed.
7271
	 *
7272
	 * @since 7.3.0
7273
	 *
7274
	 * @param int $user_id User ID being updated.
7275
	 */
7276
	public static function user_meta_cleanup( $user_id ) {
7277
		$meta_keys = array(
7278
			// AtD removed from Jetpack 7.3
7279
			'AtD_options',
7280
			'AtD_check_when',
7281
			'AtD_guess_lang',
7282
			'AtD_ignored_phrases',
7283
		);
7284
7285
		foreach ( $meta_keys as $meta_key ) {
7286
			if ( get_user_meta( $user_id, $meta_key ) ) {
7287
				delete_user_meta( $user_id, $meta_key );
7288
			}
7289
		}
7290
	}
7291
7292
	/**
7293
	 * Checks if a Jetpack site is both active and not in offline mode.
7294
	 *
7295
	 * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_offline_mode`.
7296
	 *
7297
	 * @deprecated 8.8.0
7298
	 *
7299
	 * @return bool True if Jetpack is active and not in offline mode.
7300
	 */
7301
	public static function is_active_and_not_development_mode() {
7302
		_deprecated_function( __FUNCTION__, 'jetpack-8.8.0', 'Jetpack::is_active_and_not_offline_mode' );
7303
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() ) {
7304
			return false;
7305
		}
7306
		return true;
7307
	}
7308
7309
	/**
7310
	 * Checks if a Jetpack site is both active and not in offline mode.
7311
	 *
7312
	 * This is a DRY function to avoid repeating `Jetpack::is_active && ! Automattic\Jetpack\Status->is_offline_mode`.
7313
	 *
7314
	 * @since 8.8.0
7315
	 *
7316
	 * @return bool True if Jetpack is active and not in offline mode.
7317
	 */
7318
	public static function is_active_and_not_offline_mode() {
7319
		if ( ! self::is_active() || ( new Status() )->is_offline_mode() ) {
7320
			return false;
7321
		}
7322
		return true;
7323
	}
7324
7325
	/**
7326
	 * Returns the list of products that we have available for purchase.
7327
	 */
7328
	public static function get_products_for_purchase() {
7329
		$products = array();
7330
		if ( ! is_multisite() ) {
7331
			$products[] = array(
7332
				'key'               => 'backup',
7333
				'title'             => __( 'Jetpack Backup', 'jetpack' ),
7334
				'short_description' => __( 'Always-on backups ensure you never lose your site.', 'jetpack' ),
7335
				'learn_more'        => __( 'Which backup option is best for me?', 'jetpack' ),
7336
				'description'       => __( 'Always-on backups ensure you never lose your site. Your changes are saved as you edit and you have unlimited backup archives.', 'jetpack' ),
7337
				'options_label'     => __( 'Select a backup option:', 'jetpack' ),
7338
				'options'           => array(
7339
					array(
7340
						'type'        => 'daily',
7341
						'slug'        => 'jetpack-backup-daily',
7342
						'key'         => 'jetpack_backup_daily',
7343
						'name'        => __( 'Daily Backups', 'jetpack' ),
7344
						'description' => __( 'Your data is being securely backed up daily.', 'jetpack' ),
7345
					),
7346
					array(
7347
						'type'        => 'realtime',
7348
						'slug'        => 'jetpack-backup-realtime',
7349
						'key'         => 'jetpack_backup_realtime',
7350
						'name'        => __( 'Real-Time Backups', 'jetpack' ),
7351
						'description' => __( 'Your data is being securely backed up as you edit.', 'jetpack' ),
7352
					),
7353
				),
7354
				'default_option'    => 'realtime',
7355
				'show_promotion'    => true,
7356
				'discount_percent'  => 70,
7357
				'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'daily-backup-plan', 'realtime-backup-plan' ),
7358
			);
7359
7360
			$products[] = array(
7361
				'key'               => 'scan',
7362
				'title'             => __( 'Jetpack Scan', 'jetpack' ),
7363
				'short_description' => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7364
				'learn_more'        => __( 'Learn More', 'jetpack' ),
7365
				'description'       => __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats.', 'jetpack' ),
7366
				'show_promotion'    => true,
7367
				'discount_percent'  => 30,
7368
				'options'           => array(
7369
					array(
7370
						'type'      => 'scan',
7371
						'slug'      => 'jetpack-scan',
7372
						'key'       => 'jetpack_scan',
7373
						'name'      => __( 'Daily Scan', 'jetpack' ),
7374
					),
7375
				),
7376
				'default_option'    => 'scan',
7377
				'included_in_plans' => array( 'premium-plan', 'business-plan', 'scan-plan' ),
7378
			);
7379
		}
7380
7381
		$products[] = array(
7382
			'key'               => 'search',
7383
			'title'             => __( 'Jetpack Search', 'jetpack' ),
7384
			'short_description' => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7385
			'learn_more'        => __( 'Learn More', 'jetpack' ),
7386
			'description'       => __( 'Incredibly powerful and customizable, Jetpack Search helps your visitors instantly find the right content – right when they need it.', 'jetpack' ),
7387
			'label_popup'  		=> __( 'Records are all posts, pages, custom post types, and other types of content indexed by Jetpack Search.' ),
7388
			'options'           => array(
7389
				array(
7390
					'type'      => 'search',
7391
					'slug'      => 'jetpack-search',
7392
					'key'       => 'jetpack_search',
7393
					'name'      => __( 'Search', 'jetpack' ),
7394
				),
7395
			),
7396
			'tears'             => array(),
7397
			'default_option'    => 'search',
7398
			'show_promotion'    => false,
7399
			'included_in_plans' => array( 'search-plan' ),
7400
		);
7401
7402
		$products[] = array(
7403
			'key'               => 'anti-spam',
7404
			'title'             => __( 'Jetpack Anti-Spam', 'jetpack' ),
7405
			'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' ),
7406
			'learn_more'        => __( 'Learn More', 'jetpack' ),
7407
			'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' ),
7408
			'options'           => array(
7409
				array(
7410
					'type'      => 'anti-spam',
7411
					'slug'      => 'jetpack-anti-spam',
7412
					'key'       => 'jetpack_anti_spam',
7413
					'name'      => __( 'Anti-Spam', 'jetpack' ),
7414
				),
7415
			),
7416
			'default_option'    => 'anti-spam',
7417
			'included_in_plans' => array( 'personal-plan', 'premium-plan', 'business-plan', 'anti-spam-plan' ),
7418
		);
7419
7420
		return $products;
7421
	}
7422
}
7423