Completed
Push — try/package-button-block ( 063e81 )
by
unknown
110:43 queued 103:17
created

Jetpack::deprecated_hooks()   C

Complexity

Conditions 10
Paths 54

Size

Total Lines 121

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 54
nop 0
dl 0
loc 121
rs 6.1333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Constants;
10
use Automattic\Jetpack\Partner;
11
use Automattic\Jetpack\Roles;
12
use Automattic\Jetpack\Status;
13
use Automattic\Jetpack\Sync\Functions;
14
use Automattic\Jetpack\Sync\Health;
15
use Automattic\Jetpack\Sync\Sender;
16
use Automattic\Jetpack\Sync\Users;
17
use Automattic\Jetpack\Terms_Of_Service;
18
use Automattic\Jetpack\Tracking;
19
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
20
use Automattic\Jetpack\Redirect;
21
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
22
23
/*
24
Options:
25
jetpack_options (array)
26
	An array of options.
27
	@see Jetpack_Options::get_option_names()
28
29
jetpack_register (string)
30
	Temporary verification secrets.
31
32
jetpack_activated (int)
33
	1: the plugin was activated normally
34
	2: the plugin was activated on this site because of a network-wide activation
35
	3: the plugin was auto-installed
36
	4: the plugin was manually disconnected (but is still installed)
37
38
jetpack_active_modules (array)
39
	Array of active module slugs.
40
41
jetpack_do_activate (bool)
42
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
43
*/
44
45
require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php';
46
47
class Jetpack {
48
	public $xmlrpc_server = null;
49
50
	private $rest_authentication_status = null;
51
52
	public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA']
53
54
	/**
55
	 * @var array The handles of styles that are concatenated into jetpack.css.
56
	 *
57
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
58
	 */
59
	public $concatenated_style_handles = array(
60
		'jetpack-carousel',
61
		'grunion.css',
62
		'the-neverending-homepage',
63
		'jetpack_likes',
64
		'jetpack_related-posts',
65
		'sharedaddy',
66
		'jetpack-slideshow',
67
		'presentations',
68
		'quiz',
69
		'jetpack-subscriptions',
70
		'jetpack-responsive-videos-style',
71
		'jetpack-social-menu',
72
		'tiled-gallery',
73
		'jetpack_display_posts_widget',
74
		'gravatar-profile-widget',
75
		'goodreads-widget',
76
		'jetpack_social_media_icons_widget',
77
		'jetpack-top-posts-widget',
78
		'jetpack_image_widget',
79
		'jetpack-my-community-widget',
80
		'jetpack-authors-widget',
81
		'wordads',
82
		'eu-cookie-law-style',
83
		'flickr-widget-style',
84
		'jetpack-search-widget',
85
		'jetpack-simple-payments-widget-style',
86
		'jetpack-widget-social-icons-styles',
87
	);
88
89
	/**
90
	 * Contains all assets that have had their URL rewritten to minified versions.
91
	 *
92
	 * @var array
93
	 */
94
	static $min_assets = array();
95
96
	public $plugins_to_deactivate = array(
97
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
98
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
99
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
100
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
101
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
102
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
103
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
104
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
105
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
106
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
107
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
108
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
109
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
110
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' ),
111
	);
112
113
	/**
114
	 * Map of roles we care about, and their corresponding minimum capabilities.
115
	 *
116
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::$capability_translations instead.
117
	 *
118
	 * @access public
119
	 * @static
120
	 *
121
	 * @var array
122
	 */
123
	public static $capability_translations = array(
124
		'administrator' => 'manage_options',
125
		'editor'        => 'edit_others_posts',
126
		'author'        => 'publish_posts',
127
		'contributor'   => 'edit_posts',
128
		'subscriber'    => 'read',
129
	);
130
131
	/**
132
	 * Map of modules that have conflicts with plugins and should not be auto-activated
133
	 * if the plugins are active.  Used by filter_default_modules
134
	 *
135
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
136
	 * change `module-slug` and add this to your plugin:
137
	 *
138
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
139
	 * function my_jetpack_get_default_modules( $modules ) {
140
	 *     return array_diff( $modules, array( 'module-slug' ) );
141
	 * }
142
	 *
143
	 * @var array
144
	 */
145
	private $conflicting_plugins = array(
146
		'comments'           => array(
147
			'Intense Debate'                 => 'intensedebate/intensedebate.php',
148
			'Disqus'                         => 'disqus-comment-system/disqus.php',
149
			'Livefyre'                       => 'livefyre-comments/livefyre.php',
150
			'Comments Evolved for WordPress' => 'gplus-comments/comments-evolved.php',
151
			'Google+ Comments'               => 'google-plus-comments/google-plus-comments.php',
152
			'WP-SpamShield Anti-Spam'        => 'wp-spamshield/wp-spamshield.php',
153
		),
154
		'comment-likes'      => array(
155
			'Epoch' => 'epoch/plugincore.php',
156
		),
157
		'contact-form'       => array(
158
			'Contact Form 7'           => 'contact-form-7/wp-contact-form-7.php',
159
			'Gravity Forms'            => 'gravityforms/gravityforms.php',
160
			'Contact Form Plugin'      => 'contact-form-plugin/contact_form.php',
161
			'Easy Contact Forms'       => 'easy-contact-forms/easy-contact-forms.php',
162
			'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php',
163
			'Ninja Forms'              => 'ninja-forms/ninja-forms.php',
164
		),
165
		'latex'              => array(
166
			'LaTeX for WordPress'     => 'latex/latex.php',
167
			'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php',
168
			'Easy WP LaTeX'           => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
169
			'MathJax-LaTeX'           => 'mathjax-latex/mathjax-latex.php',
170
			'Enable Latex'            => 'enable-latex/enable-latex.php',
171
			'WP QuickLaTeX'           => 'wp-quicklatex/wp-quicklatex.php',
172
		),
173
		'protect'            => array(
174
			'Limit Login Attempts'              => 'limit-login-attempts/limit-login-attempts.php',
175
			'Captcha'                           => 'captcha/captcha.php',
176
			'Brute Force Login Protection'      => 'brute-force-login-protection/brute-force-login-protection.php',
177
			'Login Security Solution'           => 'login-security-solution/login-security-solution.php',
178
			'WPSecureOps Brute Force Protect'   => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
179
			'BulletProof Security'              => 'bulletproof-security/bulletproof-security.php',
180
			'SiteGuard WP Plugin'               => 'siteguard/siteguard.php',
181
			'Security-protection'               => 'security-protection/security-protection.php',
182
			'Login Security'                    => 'login-security/login-security.php',
183
			'Botnet Attack Blocker'             => 'botnet-attack-blocker/botnet-attack-blocker.php',
184
			'Wordfence Security'                => 'wordfence/wordfence.php',
185
			'All In One WP Security & Firewall' => 'all-in-one-wp-security-and-firewall/wp-security.php',
186
			'iThemes Security'                  => 'better-wp-security/better-wp-security.php',
187
		),
188
		'random-redirect'    => array(
189
			'Random Redirect 2' => 'random-redirect-2/random-redirect.php',
190
		),
191
		'related-posts'      => array(
192
			'YARPP'                       => 'yet-another-related-posts-plugin/yarpp.php',
193
			'WordPress Related Posts'     => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
194
			'nrelate Related Content'     => 'nrelate-related-content/nrelate-related.php',
195
			'Contextual Related Posts'    => 'contextual-related-posts/contextual-related-posts.php',
196
			'Related Posts for WordPress' => 'microkids-related-posts/microkids-related-posts.php',
197
			'outbrain'                    => 'outbrain/outbrain.php',
198
			'Shareaholic'                 => 'shareaholic/shareaholic.php',
199
			'Sexybookmarks'               => 'sexybookmarks/shareaholic.php',
200
		),
201
		'sharedaddy'         => array(
202
			'AddThis'     => 'addthis/addthis_social_widget.php',
203
			'Add To Any'  => 'add-to-any/add-to-any.php',
204
			'ShareThis'   => 'share-this/sharethis.php',
205
			'Shareaholic' => 'shareaholic/shareaholic.php',
206
		),
207
		'seo-tools'          => array(
208
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
209
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
210
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
211
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
212
			'The SEO Framework'              => 'autodescription/autodescription.php',
213
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
214
		),
215
		'verification-tools' => array(
216
			'WordPress SEO by Yoast'         => 'wordpress-seo/wp-seo.php',
217
			'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
218
			'All in One SEO Pack'            => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
219
			'All in One SEO Pack Pro'        => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
220
			'The SEO Framework'              => 'autodescription/autodescription.php',
221
			'Rank Math'                      => 'seo-by-rank-math/rank-math.php',
222
		),
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
		),
245
		'lazy-images'        => array(
246
			'Lazy Load'              => 'lazy-load/lazy-load.php',
247
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
248
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
249
		),
250
	);
251
252
	/**
253
	 * Plugins for which we turn off our Facebook OG Tags implementation.
254
	 *
255
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
256
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
257
	 *
258
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
259
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
260
	 */
261
	private $open_graph_conflicting_plugins = array(
262
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
263
		// 2 Click Social Media Buttons
264
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
265
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
266
		'complete-open-graph/complete-open-graph.php',           // Complete Open Graph
267
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
268
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
269
		// Open Graph Meta Tags by Heateor
270
		'facebook/facebook.php',                                 // Facebook (official plugin)
271
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
272
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
273
		// Facebook Featured Image & OG Meta Tags
274
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
275
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
276
		// Facebook Open Graph Meta Tags for WordPress
277
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
278
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
279
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
280
		// Fedmich's Facebook Open Graph Meta
281
		'network-publisher/networkpub.php',                      // Network Publisher
282
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
283
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
284
		// NextScripts SNAP
285
		'og-tags/og-tags.php',                                   // OG Tags
286
		'opengraph/opengraph.php',                               // Open Graph
287
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
288
		// Open Graph Protocol Framework
289
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
290
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
291
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
292
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
293
		'sharepress/sharepress.php',                             // SharePress
294
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
295
		'social-discussions/social-discussions.php',             // Social Discussions
296
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
297
		'socialize/socialize.php',                               // Socialize
298
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
299
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
300
		// Tweet, Like, Google +1 and Share
301
		'wordbooker/wordbooker.php',                             // Wordbooker
302
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
303
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
304
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
305
		// WP Facebook Like Send & Open Graph Meta
306
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
307
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
308
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
309
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
310
		'open-graph-metabox/open-graph-metabox.php',              // Open Graph Metabox
311
		'seo-by-rank-math/rank-math.php',                        // Rank Math.
312
	);
313
314
	/**
315
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
316
	 */
317
	private $twitter_cards_conflicting_plugins = array(
318
		// 'twitter/twitter.php',                       // The official one handles this on its own.
319
		// https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
320
			'eewee-twitter-card/index.php',              // Eewee Twitter Card
321
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
322
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
323
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
324
		// Pure Web Brilliant's Social Graph Twitter Cards Extension
325
		'twitter-cards/twitter-cards.php',           // Twitter Cards
326
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
327
		'wp-to-twitter/wp-to-twitter.php',           // WP to Twitter
328
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
329
		'seo-by-rank-math/rank-math.php',            // Rank Math.
330
	);
331
332
	/**
333
	 * Message to display in admin_notice
334
	 *
335
	 * @var string
336
	 */
337
	public $message = '';
338
339
	/**
340
	 * Error to display in admin_notice
341
	 *
342
	 * @var string
343
	 */
344
	public $error = '';
345
346
	/**
347
	 * Modules that need more privacy description.
348
	 *
349
	 * @var string
350
	 */
351
	public $privacy_checks = '';
352
353
	/**
354
	 * Stats to record once the page loads
355
	 *
356
	 * @var array
357
	 */
358
	public $stats = array();
359
360
	/**
361
	 * Jetpack_Sync object
362
	 */
363
	public $sync;
364
365
	/**
366
	 * Verified data for JSON authorization request
367
	 */
368
	public $json_api_authorization_request = array();
369
370
	/**
371
	 * @var Automattic\Jetpack\Connection\Manager
372
	 */
373
	protected $connection_manager;
374
375
	/**
376
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
377
	 */
378
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
379
380
	/**
381
	 * Constant for login redirect key.
382
	 *
383
	 * @var string
384
	 * @since 8.4.0
385
	 */
386
	public static $jetpack_redirect_login = 'jetpack_connect_login_redirect';
387
388
	/**
389
	 * Holds the singleton instance of this class
390
	 *
391
	 * @since 2.3.3
392
	 * @var Jetpack
393
	 */
394
	static $instance = false;
395
396
	/**
397
	 * Singleton
398
	 *
399
	 * @static
400
	 */
401
	public static function init() {
402
		if ( ! self::$instance ) {
403
			self::$instance = new Jetpack();
404
			add_action( 'plugins_loaded', array( self::$instance, 'plugin_upgrade' ) );
405
		}
406
407
		return self::$instance;
408
	}
409
410
	/**
411
	 * Must never be called statically
412
	 */
413
	function plugin_upgrade() {
414
		if ( self::is_active() ) {
415
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
416
			if ( JETPACK__VERSION != $version ) {
417
				// Prevent multiple upgrades at once - only a single process should trigger
418
				// an upgrade to avoid stampedes
419
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
420
					return;
421
				}
422
423
				// Set a short lock to prevent multiple instances of the upgrade
424
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
425
426
				// check which active modules actually exist and remove others from active_modules list
427
				$unfiltered_modules = self::get_active_modules();
428
				$modules            = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
429
				if ( array_diff( $unfiltered_modules, $modules ) ) {
430
					self::update_active_modules( $modules );
431
				}
432
433
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
434
435
				// Upgrade to 4.3.0
436
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
437
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
438
				}
439
440
				// Make sure Markdown for posts gets turned back on
441
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
442
					update_option( 'wpcom_publish_posts_with_markdown', true );
443
				}
444
445
				/*
446
				 * Minileven deprecation. 8.3.0.
447
				 * Only delete options if not using
448
				 * the replacement standalone Minileven plugin.
449
				 */
450
				if (
451
					! self::is_plugin_active( 'minileven-master/minileven.php' )
452
					&& ! self::is_plugin_active( 'minileven/minileven.php' )
453
				) {
454
					if ( get_option( 'wp_mobile_custom_css' ) ) {
455
						delete_option( 'wp_mobile_custom_css' );
456
					}
457
					if ( get_option( 'wp_mobile_excerpt' ) ) {
458
						delete_option( 'wp_mobile_excerpt' );
459
					}
460
					if ( get_option( 'wp_mobile_featured_images' ) ) {
461
						delete_option( 'wp_mobile_featured_images' );
462
					}
463
					if ( get_option( 'wp_mobile_app_promos' ) ) {
464
						delete_option( 'wp_mobile_app_promos' );
465
					}
466
				}
467
468
				// Upgrade to 8.4.0.
469
				if ( Jetpack_Options::get_option( 'ab_connect_banner_green_bar' ) ) {
470
					Jetpack_Options::delete_option( 'ab_connect_banner_green_bar' );
471
				}
472
473
				if ( did_action( 'wp_loaded' ) ) {
474
					self::upgrade_on_load();
475
				} else {
476
					add_action(
477
						'wp_loaded',
478
						array( __CLASS__, 'upgrade_on_load' )
479
					);
480
				}
481
			}
482
		}
483
	}
484
485
	/**
486
	 * Runs upgrade routines that need to have modules loaded.
487
	 */
488
	static function upgrade_on_load() {
489
490
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
491
		// This can happen in case Jetpack has been just upgraded and is
492
		// being initialized late during the page load. In this case we wait
493
		// until the next proper admin page load with Jetpack active.
494
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
495
			delete_transient( self::$plugin_upgrade_lock_key );
496
497
			return;
498
		}
499
500
		self::maybe_set_version_option();
501
502
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
503
			Jetpack_Widget_Conditions::migrate_post_type_rules();
504
		}
505
506
		if (
507
			class_exists( 'Jetpack_Sitemap_Manager' )
508
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
509
		) {
510
			do_action( 'jetpack_sitemaps_purge_data' );
511
		}
512
513
		// Delete old stats cache
514
		delete_option( 'jetpack_restapi_stats_cache' );
515
516
		delete_transient( self::$plugin_upgrade_lock_key );
517
	}
518
519
	/**
520
	 * Saves all the currently active modules to options.
521
	 * Also fires Action hooks for each newly activated and deactivated module.
522
	 *
523
	 * @param $modules Array Array of active modules to be saved in options.
524
	 *
525
	 * @return $success bool true for success, false for failure.
0 ignored issues
show
Documentation introduced by
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...
526
	 */
527
	static function update_active_modules( $modules ) {
528
		$current_modules      = Jetpack_Options::get_option( 'active_modules', array() );
529
		$active_modules       = self::get_active_modules();
530
		$new_active_modules   = array_diff( $modules, $current_modules );
531
		$new_inactive_modules = array_diff( $active_modules, $modules );
532
		$new_current_modules  = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
533
		$reindexed_modules    = array_values( $new_current_modules );
534
		$success              = Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
535
536
		foreach ( $new_active_modules as $module ) {
537
			/**
538
			 * Fires when a specific module is activated.
539
			 *
540
			 * @since 1.9.0
541
			 *
542
			 * @param string $module Module slug.
543
			 * @param boolean $success whether the module was activated. @since 4.2
544
			 */
545
			do_action( 'jetpack_activate_module', $module, $success );
546
			/**
547
			 * Fires when a module is activated.
548
			 * The dynamic part of the filter, $module, is the module slug.
549
			 *
550
			 * @since 1.9.0
551
			 *
552
			 * @param string $module Module slug.
553
			 */
554
			do_action( "jetpack_activate_module_$module", $module );
555
		}
556
557
		foreach ( $new_inactive_modules as $module ) {
558
			/**
559
			 * Fired after a module has been deactivated.
560
			 *
561
			 * @since 4.2.0
562
			 *
563
			 * @param string $module Module slug.
564
			 * @param boolean $success whether the module was deactivated.
565
			 */
566
			do_action( 'jetpack_deactivate_module', $module, $success );
567
			/**
568
			 * Fires when a module is deactivated.
569
			 * The dynamic part of the filter, $module, is the module slug.
570
			 *
571
			 * @since 1.9.0
572
			 *
573
			 * @param string $module Module slug.
574
			 */
575
			do_action( "jetpack_deactivate_module_$module", $module );
576
		}
577
578
		return $success;
579
	}
580
581
	static function delete_active_modules() {
582
		self::update_active_modules( array() );
583
	}
584
585
	/**
586
	 * Adds a hook to plugins_loaded at a priority that's currently the earliest
587
	 * available.
588
	 */
589
	public function add_configure_hook() {
590
		global $wp_filter;
591
592
		$current_priority = has_filter( 'plugins_loaded', array( $this, 'configure' ) );
593
		if ( false !== $current_priority ) {
594
			remove_action( 'plugins_loaded', array( $this, 'configure' ), $current_priority );
595
		}
596
597
		$taken_priorities = array_map( 'intval', array_keys( $wp_filter['plugins_loaded']->callbacks ) );
598
		sort( $taken_priorities );
599
600
		$first_priority = array_shift( $taken_priorities );
601
602
		if ( defined( 'PHP_INT_MAX' ) && $first_priority <= - PHP_INT_MAX ) {
603
			$new_priority = - PHP_INT_MAX;
604
		} else {
605
			$new_priority = $first_priority - 1;
606
		}
607
608
		add_action( 'plugins_loaded', array( $this, 'configure' ), $new_priority );
609
	}
610
611
	/**
612
	 * Constructor.  Initializes WordPress hooks
613
	 */
614
	private function __construct() {
615
		/*
616
		 * Check for and alert any deprecated hooks
617
		 */
618
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
619
620
		// Note how this runs at an earlier plugin_loaded hook intentionally to accomodate for other plugins.
621
		add_action( 'plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
622
		add_action( 'network_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
623
		add_action( 'mu_plugin_loaded', array( $this, 'add_configure_hook' ), 90 );
624
		add_action( 'plugins_loaded', array( $this, 'late_initialization' ), 90 );
625
626
		add_action( 'jetpack_verify_signature_error', array( $this, 'track_xmlrpc_error' ) );
627
628
		add_filter(
629
			'jetpack_signature_check_token',
630
			array( __CLASS__, 'verify_onboarding_token' ),
631
			10,
632
			3
633
		);
634
635
		/**
636
		 * Prepare Gutenberg Editor functionality
637
		 */
638
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'init' ) );
639
		add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
640
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
641
642
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
643
644
		// Unlink user before deleting the user from WP.com.
645
		add_action( 'deleted_user', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
646
		add_action( 'remove_user_from_blog', array( 'Automattic\\Jetpack\\Connection\\Manager', 'disconnect_user' ), 10, 1 );
647
648
		add_action( 'jetpack_event_log', array( 'Jetpack', 'log' ), 10, 2 );
649
650
		add_filter( 'login_url', array( $this, 'login_url' ), 10, 2 );
651
		add_action( 'login_init', array( $this, 'login_init' ) );
652
653
		add_filter( 'determine_current_user', array( $this, 'wp_rest_authenticate' ) );
654
		add_filter( 'rest_authentication_errors', array( $this, 'wp_rest_authentication_errors' ) );
655
656
		add_action( 'admin_init', array( $this, 'admin_init' ) );
657
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
658
659
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ), 20 );
660
661
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
662
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
663
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
664
665
		// returns HTTPS support status
666
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
667
668
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
669
670
		add_action( 'wp_ajax_jetpack_wizard_banner', array( 'Jetpack_Wizard_Banner', 'ajax_callback' ) );
671
672
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
673
674
		/**
675
		 * These actions run checks to load additional files.
676
		 * They check for external files or plugins, so they need to run as late as possible.
677
		 */
678
		add_action( 'wp_head', array( $this, 'check_open_graph' ), 1 );
679
		add_action( 'amp_story_head', array( $this, 'check_open_graph' ), 1 );
680
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ), 999 );
681
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
682
683
		add_filter( 'plugins_url', array( 'Jetpack', 'maybe_min_asset' ), 1, 3 );
684
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2 );
685
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
686
687
		add_filter( 'profile_update', array( 'Jetpack', 'user_meta_cleanup' ) );
688
689
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
690
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
691
692
		// A filter to control all just in time messages
693
		add_filter( 'jetpack_just_in_time_msgs', '__return_true', 9 );
694
695
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9 );
696
697
		/*
698
		 * If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
699
		 * We should make sure to only do this for front end links.
700
		 */
701
		if ( self::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
702
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
703
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
704
705
			/*
706
			 * We'll override wp_notify_postauthor and wp_notify_moderator pluggable functions
707
			 * so they point moderation links on emails to Calypso.
708
			 */
709
			jetpack_require_lib( 'functions.wp-notify' );
710
		}
711
712
		add_action(
713
			'plugins_loaded',
714
			function() {
715
				if ( User_Agent_Info::is_mobile_app() ) {
716
					add_filter( 'get_edit_post_link', '__return_empty_string' );
717
				}
718
			}
719
		);
720
721
		// Update the Jetpack plan from API on heartbeats.
722
		add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
723
724
		/**
725
		 * This is the hack to concatenate all css files into one.
726
		 * For description and reasoning see the implode_frontend_css method.
727
		 *
728
		 * Super late priority so we catch all the registered styles.
729
		 */
730
		if ( ! is_admin() ) {
731
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
732
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
733
		}
734
735
		/**
736
		 * These are sync actions that we need to keep track of for jitms
737
		 */
738
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
739
740
		// Actually push the stats on shutdown.
741
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
742
			add_action( 'shutdown', array( $this, 'push_stats' ) );
743
		}
744
745
		// Actions for Manager::authorize().
746
		add_action( 'jetpack_authorize_starting', array( $this, 'authorize_starting' ) );
747
		add_action( 'jetpack_authorize_ending_linked', array( $this, 'authorize_ending_linked' ) );
748
		add_action( 'jetpack_authorize_ending_authorized', array( $this, 'authorize_ending_authorized' ) );
749
750
		// Filters for the Manager::get_token() urls and request body.
751
		add_filter( 'jetpack_token_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
752
		add_filter( 'jetpack_token_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
753
		add_filter( 'jetpack_token_request_body', array( __CLASS__, 'filter_token_request_body' ) );
754
	}
755
756
	/**
757
	 * Before everything else starts getting initalized, we need to initialize Jetpack using the
758
	 * Config object.
759
	 */
760
	public function configure() {
761
		$config = new Config();
762
763
		foreach (
764
			array(
765
				'sync',
766
				'tracking',
767
				'tos',
768
			)
769
			as $feature
770
		) {
771
			$config->ensure( $feature );
772
		}
773
774
		$config->ensure(
775
			'connection',
776
			array(
777
				'slug' => 'jetpack',
778
				'name' => 'Jetpack',
779
			)
780
		);
781
782
		if ( is_admin() ) {
783
			$config->ensure( 'jitm' );
784
		}
785
786
		if ( ! $this->connection_manager ) {
787
			$this->connection_manager = new Connection_Manager( 'jetpack' );
788
		}
789
790
		/*
791
		 * Load things that should only be in Network Admin.
792
		 *
793
		 * For now blow away everything else until a more full
794
		 * understanding of what is needed at the network level is
795
		 * available
796
		 */
797
		if ( is_multisite() ) {
798
			$network = Jetpack_Network::init();
799
			$network->set_connection( $this->connection_manager );
800
		}
801
802
		if ( $this->connection_manager->is_active() ) {
803
			add_action( 'login_form_jetpack_json_api_authorization', array( $this, 'login_form_json_api_authorization' ) );
804
805
			Jetpack_Heartbeat::init();
806
			if ( self::is_module_active( 'stats' ) && self::is_module_active( 'search' ) ) {
807
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
808
				Jetpack_Search_Performance_Logger::init();
809
			}
810
		}
811
812
		// Initialize remote file upload request handlers.
813
		$this->add_remote_request_handlers();
814
815
		/*
816
		 * Enable enhanced handling of previewing sites in Calypso
817
		 */
818
		if ( self::is_active() ) {
819
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
820
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
821
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
822
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
823
		}
824
	}
825
826
	/**
827
	 * Runs on plugins_loaded. Use this to add code that needs to be executed later than other
828
	 * initialization code.
829
	 *
830
	 * @action plugins_loaded
831
	 */
832
	public function late_initialization() {
833
		add_action( 'plugins_loaded', array( 'Jetpack', 'plugin_textdomain' ), 99 );
834
		add_action( 'plugins_loaded', array( 'Jetpack', 'load_modules' ), 100 );
835
836
		Partner::init();
837
838
		/**
839
		 * Fires when Jetpack is fully loaded and ready. This is the point where it's safe
840
		 * to instantiate classes from packages and namespaces that are managed by the Jetpack Autoloader.
841
		 *
842
		 * @since 8.1.0
843
		 *
844
		 * @param Jetpack $jetpack the main plugin class object.
845
		 */
846
		do_action( 'jetpack_loaded', $this );
847
848
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
849
	}
850
851
	/**
852
	 * Sets up the XMLRPC request handlers.
853
	 *
854
	 * @deprecated since 7.7.0
855
	 * @see Automattic\Jetpack\Connection\Manager::setup_xmlrpc_handlers()
856
	 *
857
	 * @param array                 $request_params Incoming request parameters.
858
	 * @param Boolean               $is_active      Whether the connection is currently active.
859
	 * @param Boolean               $is_signed      Whether the signature check has been successful.
860
	 * @param Jetpack_XMLRPC_Server $xmlrpc_server  (optional) An instance of the server to use instead of instantiating a new one.
0 ignored issues
show
Documentation introduced by
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...
861
	 */
862 View Code Duplication
	public function setup_xmlrpc_handlers(
863
		$request_params,
864
		$is_active,
865
		$is_signed,
866
		Jetpack_XMLRPC_Server $xmlrpc_server = null
867
	) {
868
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::setup_xmlrpc_handlers' );
869
870
		if ( ! $this->connection_manager ) {
871
			$this->connection_manager = new Connection_Manager();
872
		}
873
874
		return $this->connection_manager->setup_xmlrpc_handlers(
875
			$request_params,
876
			$is_active,
877
			$is_signed,
878
			$xmlrpc_server
879
		);
880
	}
881
882
	/**
883
	 * Initialize REST API registration connector.
884
	 *
885
	 * @deprecated since 7.7.0
886
	 * @see Automattic\Jetpack\Connection\Manager::initialize_rest_api_registration_connector()
887
	 */
888 View Code Duplication
	public function initialize_rest_api_registration_connector() {
889
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::initialize_rest_api_registration_connector' );
890
891
		if ( ! $this->connection_manager ) {
892
			$this->connection_manager = new Connection_Manager();
893
		}
894
895
		$this->connection_manager->initialize_rest_api_registration_connector();
896
	}
897
898
	/**
899
	 * This is ported over from the manage module, which has been deprecated and baked in here.
900
	 *
901
	 * @param $domains
902
	 */
903
	function add_wpcom_to_allowed_redirect_hosts( $domains ) {
904
		add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) );
905
	}
906
907
	/**
908
	 * Return $domains, with 'wordpress.com' appended.
909
	 * This is ported over from the manage module, which has been deprecated and baked in here.
910
	 *
911
	 * @param $domains
912
	 * @return array
913
	 */
914
	function allow_wpcom_domain( $domains ) {
915
		if ( empty( $domains ) ) {
916
			$domains = array();
917
		}
918
		$domains[] = 'wordpress.com';
919
		return array_unique( $domains );
920
	}
921
922
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
923
		$post = get_post( $post_id );
924
925
		if ( empty( $post ) ) {
926
			return $default_url;
927
		}
928
929
		$post_type = $post->post_type;
930
931
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
932
		// https://en.support.wordpress.com/custom-post-types/
933
		$allowed_post_types = array(
934
			'post',
935
			'page',
936
			'jetpack-portfolio',
937
			'jetpack-testimonial',
938
		);
939
940
		if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
941
			return $default_url;
942
		}
943
944
		return esc_url(
945
			Redirect::get_url(
946
				'calypso-edit-' . $post_type,
947
				array(
948
					'path' => $post_id,
949
				)
950
			)
951
		);
952
	}
953
954
	function point_edit_comment_links_to_calypso( $url ) {
955
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
956
		wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
0 ignored issues
show
Bug introduced by
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...
Unused Code introduced by
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...
957
958
		return esc_url(
959
			Redirect::get_url(
960
				'calypso-edit-comment',
961
				array(
962
					'path' => $query_args['amp;c'],
963
				)
964
			)
965
		);
966
967
	}
968
969
	function jetpack_track_last_sync_callback( $params ) {
970
		/**
971
		 * Filter to turn off jitm caching
972
		 *
973
		 * @since 5.4.0
974
		 *
975
		 * @param bool false Whether to cache just in time messages
976
		 */
977
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
978
			return $params;
979
		}
980
981
		if ( is_array( $params ) && isset( $params[0] ) ) {
982
			$option = $params[0];
983
			if ( 'active_plugins' === $option ) {
984
				// use the cache if we can, but not terribly important if it gets evicted
985
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
986
			}
987
		}
988
989
		return $params;
990
	}
991
992
	function jetpack_connection_banner_callback() {
993
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
994
995
		// Disable the banner dismiss functionality if the pre-connection prompt helpers filter is set.
996
		if (
997
			isset( $_REQUEST['dismissBanner'] ) &&
998
			! Jetpack_Connection_Banner::force_display()
999
		) {
1000
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
1001
			wp_send_json_success();
1002
		}
1003
1004
		wp_die();
1005
	}
1006
1007
	/**
1008
	 * Removes all XML-RPC methods that are not `jetpack.*`.
1009
	 * Only used in our alternate XML-RPC endpoint, where we want to
1010
	 * ensure that Core and other plugins' methods are not exposed.
1011
	 *
1012
	 * @deprecated since 7.7.0
1013
	 * @see Automattic\Jetpack\Connection\Manager::remove_non_jetpack_xmlrpc_methods()
1014
	 *
1015
	 * @param array $methods A list of registered WordPress XMLRPC methods.
1016
	 * @return array Filtered $methods
1017
	 */
1018 View Code Duplication
	public function remove_non_jetpack_xmlrpc_methods( $methods ) {
1019
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::remove_non_jetpack_xmlrpc_methods' );
1020
1021
		if ( ! $this->connection_manager ) {
1022
			$this->connection_manager = new Connection_Manager();
1023
		}
1024
1025
		return $this->connection_manager->remove_non_jetpack_xmlrpc_methods( $methods );
1026
	}
1027
1028
	/**
1029
	 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
1030
	 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
1031
	 * security/firewall policies, we provide our own alternate XML RPC API endpoint
1032
	 * which is accessible via a different URI. Most of the below is copied directly
1033
	 * from /xmlrpc.php so that we're replicating it as closely as possible.
1034
	 *
1035
	 * @deprecated since 7.7.0
1036
	 * @see Automattic\Jetpack\Connection\Manager::alternate_xmlrpc()
1037
	 */
1038 View Code Duplication
	public function alternate_xmlrpc() {
1039
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::alternate_xmlrpc' );
1040
1041
		if ( ! $this->connection_manager ) {
1042
			$this->connection_manager = new Connection_Manager();
1043
		}
1044
1045
		$this->connection_manager->alternate_xmlrpc();
1046
	}
1047
1048
	/**
1049
	 * The callback for the JITM ajax requests.
1050
	 *
1051
	 * @deprecated since 7.9.0
1052
	 */
1053
	function jetpack_jitm_ajax_callback() {
1054
		_deprecated_function( __METHOD__, 'jetpack-7.9' );
1055
	}
1056
1057
	/**
1058
	 * If there are any stats that need to be pushed, but haven't been, push them now.
1059
	 */
1060
	function push_stats() {
1061
		if ( ! empty( $this->stats ) ) {
1062
			$this->do_stats( 'server_side' );
1063
		}
1064
	}
1065
1066
	/**
1067
	 * Sets the Jetpack custom capabilities.
1068
	 *
1069
	 * @param string[] $caps    Array of the user's capabilities.
1070
	 * @param string   $cap     Capability name.
1071
	 * @param int      $user_id The user ID.
1072
	 * @param array    $args    Adds the context to the cap. Typically the object ID.
1073
	 */
1074
	public function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
1075
		$is_development_mode = ( new Status() )->is_development_mode();
1076
		switch ( $cap ) {
1077
			case 'jetpack_manage_modules':
1078
			case 'jetpack_activate_modules':
1079
			case 'jetpack_deactivate_modules':
1080
				$caps = array( 'manage_options' );
1081
				break;
1082
			case 'jetpack_configure_modules':
1083
				$caps = array( 'manage_options' );
1084
				break;
1085
			case 'jetpack_manage_autoupdates':
1086
				$caps = array(
1087
					'manage_options',
1088
					'update_plugins',
1089
				);
1090
				break;
1091
			case 'jetpack_network_admin_page':
1092
			case 'jetpack_network_settings_page':
1093
				$caps = array( 'manage_network_plugins' );
1094
				break;
1095
			case 'jetpack_network_sites_page':
1096
				$caps = array( 'manage_sites' );
1097
				break;
1098
			case 'jetpack_admin_page':
1099
				if ( $is_development_mode ) {
1100
					$caps = array( 'manage_options' );
1101
					break;
1102
				} else {
1103
					$caps = array( 'read' );
1104
				}
1105
				break;
1106
		}
1107
		return $caps;
1108
	}
1109
1110
	/**
1111
	 * Require a Jetpack authentication.
1112
	 *
1113
	 * @deprecated since 7.7.0
1114
	 * @see Automattic\Jetpack\Connection\Manager::require_jetpack_authentication()
1115
	 */
1116 View Code Duplication
	public function require_jetpack_authentication() {
1117
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::require_jetpack_authentication' );
1118
1119
		if ( ! $this->connection_manager ) {
1120
			$this->connection_manager = new Connection_Manager();
1121
		}
1122
1123
		$this->connection_manager->require_jetpack_authentication();
1124
	}
1125
1126
	/**
1127
	 * Load language files
1128
	 *
1129
	 * @action plugins_loaded
1130
	 */
1131
	public static function plugin_textdomain() {
1132
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
1133
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
1134
	}
1135
1136
	/**
1137
	 * Register assets for use in various modules and the Jetpack admin page.
1138
	 *
1139
	 * @uses wp_script_is, wp_register_script, plugins_url
1140
	 * @action wp_loaded
1141
	 * @return null
1142
	 */
1143
	public function register_assets() {
1144 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1145
			wp_register_script(
1146
				'jetpack-gallery-settings',
1147
				Assets::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1148
				array( 'media-views' ),
1149
				'20121225'
1150
			);
1151
		}
1152
1153
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1154
			wp_register_script(
1155
				'jetpack-twitter-timeline',
1156
				Assets::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1157
				array( 'jquery' ),
1158
				'4.0.0',
1159
				true
1160
			);
1161
		}
1162
1163
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1164
			wp_register_script(
1165
				'jetpack-facebook-embed',
1166
				Assets::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1167
				array(),
1168
				null,
1169
				true
1170
			);
1171
1172
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1173
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1174
			if ( ! is_numeric( $fb_app_id ) ) {
1175
				$fb_app_id = '';
1176
			}
1177
			wp_localize_script(
1178
				'jetpack-facebook-embed',
1179
				'jpfbembed',
1180
				array(
1181
					'appid'  => $fb_app_id,
1182
					'locale' => $this->get_locale(),
1183
				)
1184
			);
1185
		}
1186
1187
		/**
1188
		 * As jetpack_register_genericons is by default fired off a hook,
1189
		 * the hook may have already fired by this point.
1190
		 * So, let's just trigger it manually.
1191
		 */
1192
		require_once JETPACK__PLUGIN_DIR . '_inc/genericons.php';
1193
		jetpack_register_genericons();
1194
1195
		/**
1196
		 * Register the social logos
1197
		 */
1198
		require_once JETPACK__PLUGIN_DIR . '_inc/social-logos.php';
1199
		jetpack_register_social_logos();
1200
1201 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
1202
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1203
		}
1204
	}
1205
1206
	/**
1207
	 * Guess locale from language code.
1208
	 *
1209
	 * @param string $lang Language code.
1210
	 * @return string|bool
1211
	 */
1212 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1213
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1214
			return 'en_US';
1215
		}
1216
1217
		if ( ! class_exists( 'GP_Locales' ) ) {
1218
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1219
				return false;
1220
			}
1221
1222
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1223
		}
1224
1225
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1226
			// WP.com: get_locale() returns 'it'
1227
			$locale = GP_Locales::by_slug( $lang );
1228
		} else {
1229
			// Jetpack: get_locale() returns 'it_IT';
1230
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1231
		}
1232
1233
		if ( ! $locale ) {
1234
			return false;
1235
		}
1236
1237
		if ( empty( $locale->facebook_locale ) ) {
1238
			if ( empty( $locale->wp_locale ) ) {
1239
				return false;
1240
			} else {
1241
				// Facebook SDK is smart enough to fall back to en_US if a
1242
				// locale isn't supported. Since supported Facebook locales
1243
				// can fall out of sync, we'll attempt to use the known
1244
				// wp_locale value and rely on said fallback.
1245
				return $locale->wp_locale;
1246
			}
1247
		}
1248
1249
		return $locale->facebook_locale;
1250
	}
1251
1252
	/**
1253
	 * Get the locale.
1254
	 *
1255
	 * @return string|bool
1256
	 */
1257
	function get_locale() {
1258
		$locale = $this->guess_locale_from_lang( get_locale() );
1259
1260
		if ( ! $locale ) {
1261
			$locale = 'en_US';
1262
		}
1263
1264
		return $locale;
1265
	}
1266
1267
	/**
1268
	 * Return the network_site_url so that .com knows what network this site is a part of.
1269
	 *
1270
	 * @param  bool $option
1271
	 * @return string
1272
	 */
1273
	public function jetpack_main_network_site_option( $option ) {
1274
		return network_site_url();
1275
	}
1276
	/**
1277
	 * Network Name.
1278
	 */
1279
	static function network_name( $option = null ) {
1280
		global $current_site;
1281
		return $current_site->site_name;
1282
	}
1283
	/**
1284
	 * Does the network allow new user and site registrations.
1285
	 *
1286
	 * @return string
1287
	 */
1288
	static function network_allow_new_registrations( $option = null ) {
1289
		return ( in_array( get_site_option( 'registration' ), array( 'none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration' ) : 'none' );
1290
	}
1291
	/**
1292
	 * Does the network allow admins to add new users.
1293
	 *
1294
	 * @return boolian
1295
	 */
1296
	static function network_add_new_users( $option = null ) {
1297
		return (bool) get_site_option( 'add_new_users' );
1298
	}
1299
	/**
1300
	 * File upload psace left per site in MB.
1301
	 *  -1 means NO LIMIT.
1302
	 *
1303
	 * @return number
1304
	 */
1305
	static function network_site_upload_space( $option = null ) {
1306
		// value in MB
1307
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1308
	}
1309
1310
	/**
1311
	 * Network allowed file types.
1312
	 *
1313
	 * @return string
1314
	 */
1315
	static function network_upload_file_types( $option = null ) {
1316
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1317
	}
1318
1319
	/**
1320
	 * Maximum file upload size set by the network.
1321
	 *
1322
	 * @return number
1323
	 */
1324
	static function network_max_upload_file_size( $option = null ) {
1325
		// value in KB
1326
		return get_site_option( 'fileupload_maxk', 300 );
1327
	}
1328
1329
	/**
1330
	 * Lets us know if a site allows admins to manage the network.
1331
	 *
1332
	 * @return array
1333
	 */
1334
	static function network_enable_administration_menus( $option = null ) {
1335
		return get_site_option( 'menu_items' );
1336
	}
1337
1338
	/**
1339
	 * If a user has been promoted to or demoted from admin, we need to clear the
1340
	 * jetpack_other_linked_admins transient.
1341
	 *
1342
	 * @since 4.3.2
1343
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1344
	 *
1345
	 * @param int    $user_id   The user ID whose role changed.
1346
	 * @param string $role      The new role.
1347
	 * @param array  $old_roles An array of the user's previous roles.
0 ignored issues
show
Documentation introduced by
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...
1348
	 */
1349
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1350
		if ( 'administrator' == $role
1351
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1352
			|| is_null( $old_roles )
1353
		) {
1354
			delete_transient( 'jetpack_other_linked_admins' );
1355
		}
1356
	}
1357
1358
	/**
1359
	 * Checks to see if there are any other users available to become primary
1360
	 * Users must both:
1361
	 * - Be linked to wpcom
1362
	 * - Be an admin
1363
	 *
1364
	 * @return mixed False if no other users are linked, Int if there are.
1365
	 */
1366
	static function get_other_linked_admins() {
1367
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1368
1369
		if ( false === $other_linked_users ) {
1370
			$admins = get_users( array( 'role' => 'administrator' ) );
1371
			if ( count( $admins ) > 1 ) {
1372
				$available = array();
1373
				foreach ( $admins as $admin ) {
1374
					if ( self::is_user_connected( $admin->ID ) ) {
1375
						$available[] = $admin->ID;
1376
					}
1377
				}
1378
1379
				$count_connected_admins = count( $available );
1380
				if ( count( $available ) > 1 ) {
1381
					$other_linked_users = $count_connected_admins;
1382
				} else {
1383
					$other_linked_users = 0;
1384
				}
1385
			} else {
1386
				$other_linked_users = 0;
1387
			}
1388
1389
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1390
		}
1391
1392
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1393
	}
1394
1395
	/**
1396
	 * Return whether we are dealing with a multi network setup or not.
1397
	 * The reason we are type casting this is because we want to avoid the situation where
1398
	 * the result is false since when is_main_network_option return false it cases
1399
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1400
	 * database which could be set to anything as opposed to what this function returns.
1401
	 *
1402
	 * @param  bool $option
1403
	 *
1404
	 * @return boolean
1405
	 */
1406
	public function is_main_network_option( $option ) {
1407
		// return '1' or ''
1408
		return (string) (bool) self::is_multi_network();
1409
	}
1410
1411
	/**
1412
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1413
	 *
1414
	 * @param  string $option
1415
	 * @return boolean
1416
	 */
1417
	public function is_multisite( $option ) {
1418
		return (string) (bool) is_multisite();
1419
	}
1420
1421
	/**
1422
	 * Implemented since there is no core is multi network function
1423
	 * Right now there is no way to tell if we which network is the dominant network on the system
1424
	 *
1425
	 * @since  3.3
1426
	 * @return boolean
1427
	 */
1428 View Code Duplication
	public static function is_multi_network() {
1429
		global  $wpdb;
1430
1431
		// if we don't have a multi site setup no need to do any more
1432
		if ( ! is_multisite() ) {
1433
			return false;
1434
		}
1435
1436
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1437
		if ( $num_sites > 1 ) {
1438
			return true;
1439
		} else {
1440
			return false;
1441
		}
1442
	}
1443
1444
	/**
1445
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1446
	 *
1447
	 * @return null
1448
	 */
1449
	function update_jetpack_main_network_site_option() {
1450
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1451
	}
1452
	/**
1453
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1454
	 */
1455
	function update_jetpack_network_settings() {
1456
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1457
		// Only sync this info for the main network site.
1458
	}
1459
1460
	/**
1461
	 * Get back if the current site is single user site.
1462
	 *
1463
	 * @return bool
1464
	 */
1465 View Code Duplication
	public static function is_single_user_site() {
1466
		global $wpdb;
1467
1468
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1469
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1470
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1471
		}
1472
		return 1 === (int) $some_users;
1473
	}
1474
1475
	/**
1476
	 * Returns true if the site has file write access false otherwise.
1477
	 *
1478
	 * @return string ( '1' | '0' )
1479
	 **/
1480
	public static function file_system_write_access() {
1481
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1482
			require_once ABSPATH . 'wp-admin/includes/file.php';
1483
		}
1484
1485
		require_once ABSPATH . 'wp-admin/includes/template.php';
1486
1487
		$filesystem_method = get_filesystem_method();
1488
		if ( $filesystem_method === 'direct' ) {
1489
			return 1;
1490
		}
1491
1492
		ob_start();
1493
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1494
		ob_end_clean();
1495
		if ( $filesystem_credentials_are_stored ) {
1496
			return 1;
1497
		}
1498
		return 0;
1499
	}
1500
1501
	/**
1502
	 * Finds out if a site is using a version control system.
1503
	 *
1504
	 * @return string ( '1' | '0' )
1505
	 **/
1506
	public static function is_version_controlled() {
1507
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' );
1508
		return (string) (int) Functions::is_version_controlled();
1509
	}
1510
1511
	/**
1512
	 * Determines whether the current theme supports featured images or not.
1513
	 *
1514
	 * @return string ( '1' | '0' )
1515
	 */
1516
	public static function featured_images_enabled() {
1517
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1518
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1519
	}
1520
1521
	/**
1522
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1523
	 *
1524
	 * @deprecated 4.7 use get_avatar_url instead.
1525
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1526
	 * @param int               $size Size of the avatar image
1527
	 * @param string            $default URL to a default image to use if no avatar is available
1528
	 * @param bool              $force_display Whether to force it to return an avatar even if show_avatars is disabled
1529
	 *
1530
	 * @return array
1531
	 */
1532
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1533
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1534
		return get_avatar_url(
1535
			$id_or_email,
1536
			array(
1537
				'size'          => $size,
1538
				'default'       => $default,
1539
				'force_default' => $force_display,
1540
			)
1541
		);
1542
	}
1543
1544
	/**
1545
	 * jetpack_updates is saved in the following schema:
1546
	 *
1547
	 * array (
1548
	 *      'plugins'                       => (int) Number of plugin updates available.
1549
	 *      'themes'                        => (int) Number of theme updates available.
1550
	 *      'wordpress'                     => (int) Number of WordPress core updates available. // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
1551
	 *      'translations'                  => (int) Number of translation updates available.
1552
	 *      'total'                         => (int) Total of all available updates.
1553
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1554
	 * )
1555
	 *
1556
	 * @return array
1557
	 */
1558
	public static function get_updates() {
1559
		$update_data = wp_get_update_data();
1560
1561
		// Stores the individual update counts as well as the total count.
1562
		if ( isset( $update_data['counts'] ) ) {
1563
			$updates = $update_data['counts'];
1564
		}
1565
1566
		// If we need to update WordPress core, let's find the latest version number.
1567 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1568
			$cur = get_preferred_from_update_core();
1569
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1570
				$updates['wp_update_version'] = $cur->current;
1571
			}
1572
		}
1573
		return isset( $updates ) ? $updates : array();
1574
	}
1575
1576
	public static function get_update_details() {
1577
		$update_details = array(
1578
			'update_core'    => get_site_transient( 'update_core' ),
1579
			'update_plugins' => get_site_transient( 'update_plugins' ),
1580
			'update_themes'  => get_site_transient( 'update_themes' ),
1581
		);
1582
		return $update_details;
1583
	}
1584
1585
	public static function refresh_update_data() {
1586
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1587
1588
	}
1589
1590
	public static function refresh_theme_data() {
1591
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1592
	}
1593
1594
	/**
1595
	 * Is Jetpack active?
1596
	 * The method only checks if there's an existing token for the master user. It doesn't validate the token.
1597
	 *
1598
	 * @return bool
1599
	 */
1600
	public static function is_active() {
1601
		return self::connection()->is_active();
1602
	}
1603
1604
	/**
1605
	 * Make an API call to WordPress.com for plan status
1606
	 *
1607
	 * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom.
1608
	 *
1609
	 * @return bool True if plan is updated, false if no update
1610
	 */
1611
	public static function refresh_active_plan_from_wpcom() {
1612
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' );
1613
		return Jetpack_Plan::refresh_from_wpcom();
1614
	}
1615
1616
	/**
1617
	 * Get the plan that this Jetpack site is currently using
1618
	 *
1619
	 * @deprecated 7.2.0 Use Jetpack_Plan::get.
1620
	 * @return array Active Jetpack plan details.
1621
	 */
1622
	public static function get_active_plan() {
1623
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' );
1624
		return Jetpack_Plan::get();
1625
	}
1626
1627
	/**
1628
	 * Determine whether the active plan supports a particular feature
1629
	 *
1630
	 * @deprecated 7.2.0 Use Jetpack_Plan::supports.
1631
	 * @return bool True if plan supports feature, false if not.
1632
	 */
1633
	public static function active_plan_supports( $feature ) {
1634
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' );
1635
		return Jetpack_Plan::supports( $feature );
1636
	}
1637
1638
	/**
1639
	 * Deprecated: Is Jetpack in development (offline) mode?
1640
	 *
1641
	 * This static method is being left here intentionally without the use of _deprecated_function(), as other plugins
1642
	 * and themes still use it, and we do not want to flood them with notices.
1643
	 *
1644
	 * Please use Automattic\Jetpack\Status()->is_development_mode() instead.
1645
	 *
1646
	 * @deprecated since 8.0.
1647
	 */
1648
	public static function is_development_mode() {
1649
		return ( new Status() )->is_development_mode();
1650
	}
1651
1652
	/**
1653
	 * Whether the site is currently onboarding or not.
1654
	 * A site is considered as being onboarded if it currently has an onboarding token.
1655
	 *
1656
	 * @since 5.8
1657
	 *
1658
	 * @access public
1659
	 * @static
1660
	 *
1661
	 * @return bool True if the site is currently onboarding, false otherwise
1662
	 */
1663
	public static function is_onboarding() {
1664
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1665
	}
1666
1667
	/**
1668
	 * Determines reason for Jetpack development mode.
1669
	 */
1670
	public static function development_mode_trigger_text() {
1671
		if ( ! ( new Status() )->is_development_mode() ) {
1672
			return __( 'Jetpack is not in Development Mode.', 'jetpack' );
1673
		}
1674
1675
		if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1676
			$notice = __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1677
		} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1678
			$notice = __( 'The site URL lacking a dot (e.g. http://localhost).', 'jetpack' );
1679
		} else {
1680
			$notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1681
		}
1682
1683
		return $notice;
1684
1685
	}
1686
	/**
1687
	 * Get Jetpack development mode notice text and notice class.
1688
	 *
1689
	 * Mirrors the checks made in Automattic\Jetpack\Status->is_development_mode
1690
	 */
1691
	public static function show_development_mode_notice() {
1692 View Code Duplication
		if ( ( new Status() )->is_development_mode() ) {
1693
			$notice = sprintf(
1694
				/* translators: %s is a URL */
1695
				__( 'In <a href="%s" target="_blank">Development Mode</a>:', 'jetpack' ),
1696
				Redirect::get_url( 'jetpack-support-development-mode' )
1697
			);
1698
1699
			$notice .= ' ' . self::development_mode_trigger_text();
1700
1701
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1702
		}
1703
1704
		// Throw up a notice if using a development version and as for feedback.
1705
		if ( self::is_development_version() ) {
1706
			/* translators: %s is a URL */
1707
			$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' ) );
1708
1709
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1710
		}
1711
		// Throw up a notice if using staging mode
1712 View Code Duplication
		if ( ( new Status() )->is_staging_site() ) {
1713
			/* translators: %s is a URL */
1714
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), Redirect::get_url( 'jetpack-support-staging-sites' ) );
1715
1716
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1717
		}
1718
	}
1719
1720
	/**
1721
	 * Whether Jetpack's version maps to a public release, or a development version.
1722
	 */
1723
	public static function is_development_version() {
1724
		/**
1725
		 * Allows filtering whether this is a development version of Jetpack.
1726
		 *
1727
		 * This filter is especially useful for tests.
1728
		 *
1729
		 * @since 4.3.0
1730
		 *
1731
		 * @param bool $development_version Is this a develoment version of Jetpack?
1732
		 */
1733
		return (bool) apply_filters(
1734
			'jetpack_development_version',
1735
			! preg_match( '/^\d+(\.\d+)+$/', Constants::get_constant( 'JETPACK__VERSION' ) )
1736
		);
1737
	}
1738
1739
	/**
1740
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1741
	 */
1742
	public static function is_user_connected( $user_id = false ) {
1743
		return self::connection()->is_user_connected( $user_id );
1744
	}
1745
1746
	/**
1747
	 * Get the wpcom user data of the current|specified connected user.
1748
	 */
1749 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1750
		// TODO: remove in favor of Connection_Manager->get_connected_user_data
1751
		if ( ! $user_id ) {
1752
			$user_id = get_current_user_id();
1753
		}
1754
1755
		$transient_key = "jetpack_connected_user_data_$user_id";
1756
1757
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1758
			return $cached_user_data;
1759
		}
1760
1761
		$xml = new Jetpack_IXR_Client(
1762
			array(
1763
				'user_id' => $user_id,
1764
			)
1765
		);
1766
		$xml->query( 'wpcom.getUser' );
1767
		if ( ! $xml->isError() ) {
1768
			$user_data = $xml->getResponse();
1769
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1770
			return $user_data;
1771
		}
1772
1773
		return false;
1774
	}
1775
1776
	/**
1777
	 * Get the wpcom email of the current|specified connected user.
1778
	 */
1779
	public static function get_connected_user_email( $user_id = null ) {
1780
		if ( ! $user_id ) {
1781
			$user_id = get_current_user_id();
1782
		}
1783
1784
		$xml = new Jetpack_IXR_Client(
1785
			array(
1786
				'user_id' => $user_id,
1787
			)
1788
		);
1789
		$xml->query( 'wpcom.getUserEmail' );
1790
		if ( ! $xml->isError() ) {
1791
			return $xml->getResponse();
1792
		}
1793
		return false;
1794
	}
1795
1796
	/**
1797
	 * Get the wpcom email of the master user.
1798
	 */
1799
	public static function get_master_user_email() {
1800
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1801
		if ( $master_user_id ) {
1802
			return self::get_connected_user_email( $master_user_id );
1803
		}
1804
		return '';
1805
	}
1806
1807
	/**
1808
	 * Whether the current user is the connection owner.
1809
	 *
1810
	 * @deprecated since 7.7
1811
	 *
1812
	 * @return bool Whether the current user is the connection owner.
1813
	 */
1814
	public function current_user_is_connection_owner() {
1815
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::is_connection_owner' );
1816
		return self::connection()->is_connection_owner();
1817
	}
1818
1819
	/**
1820
	 * Gets current user IP address.
1821
	 *
1822
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1823
	 *
1824
	 * @return string                  Current user IP address.
1825
	 */
1826
	public static function current_user_ip( $check_all_headers = false ) {
1827
		if ( $check_all_headers ) {
1828
			foreach ( array(
1829
				'HTTP_CF_CONNECTING_IP',
1830
				'HTTP_CLIENT_IP',
1831
				'HTTP_X_FORWARDED_FOR',
1832
				'HTTP_X_FORWARDED',
1833
				'HTTP_X_CLUSTER_CLIENT_IP',
1834
				'HTTP_FORWARDED_FOR',
1835
				'HTTP_FORWARDED',
1836
				'HTTP_VIA',
1837
			) as $key ) {
1838
				if ( ! empty( $_SERVER[ $key ] ) ) {
1839
					return $_SERVER[ $key ];
1840
				}
1841
			}
1842
		}
1843
1844
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1845
	}
1846
1847
	/**
1848
	 * Synchronize connected user role changes
1849
	 */
1850
	function user_role_change( $user_id ) {
1851
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' );
1852
		Users::user_role_change( $user_id );
1853
	}
1854
1855
	/**
1856
	 * Loads the currently active modules.
1857
	 */
1858
	public static function load_modules() {
1859
		$is_development_mode = ( new Status() )->is_development_mode();
1860
		if (
1861
			! self::is_active()
1862
			&& ! $is_development_mode
1863
			&& ! self::is_onboarding()
1864
			&& (
1865
				! is_multisite()
1866
				|| ! get_site_option( 'jetpack_protect_active' )
1867
			)
1868
		) {
1869
			return;
1870
		}
1871
1872
		$version = Jetpack_Options::get_option( 'version' );
1873 View Code Duplication
		if ( ! $version ) {
1874
			$version = $old_version = JETPACK__VERSION . ':' . time();
1875
			/** This action is documented in class.jetpack.php */
1876
			do_action( 'updating_jetpack_version', $version, false );
1877
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1878
		}
1879
		list( $version ) = explode( ':', $version );
1880
1881
		$modules = array_filter( self::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1882
1883
		$modules_data = array();
1884
1885
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1886
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1887
			$updated_modules = array();
1888
			foreach ( $modules as $module ) {
1889
				$modules_data[ $module ] = self::get_module( $module );
1890
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1891
					continue;
1892
				}
1893
1894
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1895
					continue;
1896
				}
1897
1898
				$updated_modules[] = $module;
1899
			}
1900
1901
			$modules = array_diff( $modules, $updated_modules );
1902
		}
1903
1904
		foreach ( $modules as $index => $module ) {
1905
			// If we're in dev mode, disable modules requiring a connection
1906
			if ( $is_development_mode ) {
1907
				// Prime the pump if we need to
1908
				if ( empty( $modules_data[ $module ] ) ) {
1909
					$modules_data[ $module ] = self::get_module( $module );
1910
				}
1911
				// If the module requires a connection, but we're in local mode, don't include it.
1912
				if ( $modules_data[ $module ]['requires_connection'] ) {
1913
					continue;
1914
				}
1915
			}
1916
1917
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1918
				continue;
1919
			}
1920
1921
			if ( ! include_once self::get_module_path( $module ) ) {
1922
				unset( $modules[ $index ] );
1923
				self::update_active_modules( array_values( $modules ) );
1924
				continue;
1925
			}
1926
1927
			/**
1928
			 * Fires when a specific module is loaded.
1929
			 * The dynamic part of the hook, $module, is the module slug.
1930
			 *
1931
			 * @since 1.1.0
1932
			 */
1933
			do_action( 'jetpack_module_loaded_' . $module );
1934
		}
1935
1936
		/**
1937
		 * Fires when all the modules are loaded.
1938
		 *
1939
		 * @since 1.1.0
1940
		 */
1941
		do_action( 'jetpack_modules_loaded' );
1942
1943
		// 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.
1944
		require_once JETPACK__PLUGIN_DIR . 'modules/module-extras.php';
1945
	}
1946
1947
	/**
1948
	 * Check if Jetpack's REST API compat file should be included
1949
	 *
1950
	 * @action plugins_loaded
1951
	 * @return null
1952
	 */
1953
	public function check_rest_api_compat() {
1954
		/**
1955
		 * Filters the list of REST API compat files to be included.
1956
		 *
1957
		 * @since 2.2.5
1958
		 *
1959
		 * @param array $args Array of REST API compat files to include.
1960
		 */
1961
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1962
1963
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include ) {
1964
			require_once $_jetpack_rest_api_compat_include;
1965
		}
1966
	}
1967
1968
	/**
1969
	 * Gets all plugins currently active in values, regardless of whether they're
1970
	 * traditionally activated or network activated.
1971
	 *
1972
	 * @todo Store the result in core's object cache maybe?
1973
	 */
1974
	public static function get_active_plugins() {
1975
		$active_plugins = (array) get_option( 'active_plugins', array() );
1976
1977
		if ( is_multisite() ) {
1978
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1979
			// whereas active_plugins stores them in the values.
1980
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1981
			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...
1982
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1983
			}
1984
		}
1985
1986
		sort( $active_plugins );
1987
1988
		return array_unique( $active_plugins );
1989
	}
1990
1991
	/**
1992
	 * Gets and parses additional plugin data to send with the heartbeat data
1993
	 *
1994
	 * @since 3.8.1
1995
	 *
1996
	 * @return array Array of plugin data
1997
	 */
1998
	public static function get_parsed_plugin_data() {
1999
		if ( ! function_exists( 'get_plugins' ) ) {
2000
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
2001
		}
2002
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
2003
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
2004
		$active_plugins = self::get_active_plugins();
2005
2006
		$plugins = array();
2007
		foreach ( $all_plugins as $path => $plugin_data ) {
2008
			$plugins[ $path ] = array(
2009
				'is_active' => in_array( $path, $active_plugins ),
2010
				'file'      => $path,
2011
				'name'      => $plugin_data['Name'],
2012
				'version'   => $plugin_data['Version'],
2013
				'author'    => $plugin_data['Author'],
2014
			);
2015
		}
2016
2017
		return $plugins;
2018
	}
2019
2020
	/**
2021
	 * Gets and parses theme data to send with the heartbeat data
2022
	 *
2023
	 * @since 3.8.1
2024
	 *
2025
	 * @return array Array of theme data
2026
	 */
2027
	public static function get_parsed_theme_data() {
2028
		$all_themes  = wp_get_themes( array( 'allowed' => true ) );
2029
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2030
2031
		$themes = array();
2032
		foreach ( $all_themes as $slug => $theme_data ) {
2033
			$theme_headers = array();
2034
			foreach ( $header_keys as $header_key ) {
2035
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2036
			}
2037
2038
			$themes[ $slug ] = array(
2039
				'is_active_theme' => $slug == wp_get_theme()->get_template(),
2040
				'slug'            => $slug,
2041
				'theme_root'      => $theme_data->get_theme_root_uri(),
2042
				'parent'          => $theme_data->parent(),
2043
				'headers'         => $theme_headers,
2044
			);
2045
		}
2046
2047
		return $themes;
2048
	}
2049
2050
	/**
2051
	 * Checks whether a specific plugin is active.
2052
	 *
2053
	 * We don't want to store these in a static variable, in case
2054
	 * there are switch_to_blog() calls involved.
2055
	 */
2056
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2057
		return in_array( $plugin, self::get_active_plugins() );
2058
	}
2059
2060
	/**
2061
	 * Check if Jetpack's Open Graph tags should be used.
2062
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2063
	 *
2064
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2065
	 * @action plugins_loaded
2066
	 * @return null
2067
	 */
2068
	public function check_open_graph() {
2069
		if ( in_array( 'publicize', self::get_active_modules() ) || in_array( 'sharedaddy', self::get_active_modules() ) ) {
2070
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2071
		}
2072
2073
		$active_plugins = self::get_active_plugins();
2074
2075
		if ( ! empty( $active_plugins ) ) {
2076
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2077
				if ( in_array( $plugin, $active_plugins ) ) {
2078
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2079
					break;
2080
				}
2081
			}
2082
		}
2083
2084
		/**
2085
		 * Allow the addition of Open Graph Meta Tags to all pages.
2086
		 *
2087
		 * @since 2.0.3
2088
		 *
2089
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2090
		 */
2091
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2092
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2093
		}
2094
	}
2095
2096
	/**
2097
	 * Check if Jetpack's Twitter tags should be used.
2098
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2099
	 *
2100
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2101
	 * @action plugins_loaded
2102
	 * @return null
2103
	 */
2104
	public function check_twitter_tags() {
2105
2106
		$active_plugins = self::get_active_plugins();
2107
2108
		if ( ! empty( $active_plugins ) ) {
2109
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2110
				if ( in_array( $plugin, $active_plugins ) ) {
2111
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2112
					break;
2113
				}
2114
			}
2115
		}
2116
2117
		/**
2118
		 * Allow Twitter Card Meta tags to be disabled.
2119
		 *
2120
		 * @since 2.6.0
2121
		 *
2122
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2123
		 */
2124
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2125
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2126
		}
2127
	}
2128
2129
	/**
2130
	 * Allows plugins to submit security reports.
2131
	 *
2132
	 * @param string $type         Report type (login_form, backup, file_scanning, spam)
2133
	 * @param string $plugin_file  Plugin __FILE__, so that we can pull plugin data
2134
	 * @param array  $args         See definitions above
2135
	 */
2136
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2137
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2138
	}
2139
2140
	/* Jetpack Options API */
2141
2142
	public static function get_option_names( $type = 'compact' ) {
2143
		return Jetpack_Options::get_option_names( $type );
2144
	}
2145
2146
	/**
2147
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2148
	 *
2149
	 * @param string $name    Option name
2150
	 * @param mixed  $default (optional)
2151
	 */
2152
	public static function get_option( $name, $default = false ) {
2153
		return Jetpack_Options::get_option( $name, $default );
2154
	}
2155
2156
	/**
2157
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2158
	 *
2159
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2160
	 * @param string $name  Option name
2161
	 * @param mixed  $value Option value
2162
	 */
2163
	public static function update_option( $name, $value ) {
2164
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2165
		return Jetpack_Options::update_option( $name, $value );
2166
	}
2167
2168
	/**
2169
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2170
	 *
2171
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2172
	 * @param array $array array( option name => option value, ... )
2173
	 */
2174
	public static function update_options( $array ) {
2175
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2176
		return Jetpack_Options::update_options( $array );
2177
	}
2178
2179
	/**
2180
	 * Deletes the given option.  May be passed multiple option names as an array.
2181
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2182
	 *
2183
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2184
	 * @param string|array $names
2185
	 */
2186
	public static function delete_option( $names ) {
2187
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2188
		return Jetpack_Options::delete_option( $names );
2189
	}
2190
2191
	/**
2192
	 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::update_user_token() instead.
2193
	 *
2194
	 * Enters a user token into the user_tokens option
2195
	 *
2196
	 * @param int    $user_id The user id.
2197
	 * @param string $token The user token.
2198
	 * @param bool   $is_master_user Whether the user is the master user.
2199
	 * @return bool
2200
	 */
2201
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2202
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::update_user_token' );
2203
		return Connection_Utils::update_user_token( $user_id, $token, $is_master_user );
2204
	}
2205
2206
	/**
2207
	 * Returns an array of all PHP files in the specified absolute path.
2208
	 * Equivalent to glob( "$absolute_path/*.php" ).
2209
	 *
2210
	 * @param string $absolute_path The absolute path of the directory to search.
2211
	 * @return array Array of absolute paths to the PHP files.
2212
	 */
2213
	public static function glob_php( $absolute_path ) {
2214
		if ( function_exists( 'glob' ) ) {
2215
			return glob( "$absolute_path/*.php" );
2216
		}
2217
2218
		$absolute_path = untrailingslashit( $absolute_path );
2219
		$files         = array();
2220
		if ( ! $dir = @opendir( $absolute_path ) ) {
2221
			return $files;
2222
		}
2223
2224
		while ( false !== $file = readdir( $dir ) ) {
2225
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2226
				continue;
2227
			}
2228
2229
			$file = "$absolute_path/$file";
2230
2231
			if ( ! is_file( $file ) ) {
2232
				continue;
2233
			}
2234
2235
			$files[] = $file;
2236
		}
2237
2238
		closedir( $dir );
2239
2240
		return $files;
2241
	}
2242
2243
	public static function activate_new_modules( $redirect = false ) {
2244
		if ( ! self::is_active() && ! ( new Status() )->is_development_mode() ) {
2245
			return;
2246
		}
2247
2248
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2249 View Code Duplication
		if ( ! $jetpack_old_version ) {
2250
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2251
			/** This action is documented in class.jetpack.php */
2252
			do_action( 'updating_jetpack_version', $version, false );
2253
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2254
		}
2255
2256
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2257
2258
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2259
			return;
2260
		}
2261
2262
		$active_modules     = self::get_active_modules();
2263
		$reactivate_modules = array();
2264
		foreach ( $active_modules as $active_module ) {
2265
			$module = self::get_module( $active_module );
2266
			if ( ! isset( $module['changed'] ) ) {
2267
				continue;
2268
			}
2269
2270
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2271
				continue;
2272
			}
2273
2274
			$reactivate_modules[] = $active_module;
2275
			self::deactivate_module( $active_module );
2276
		}
2277
2278
		$new_version = JETPACK__VERSION . ':' . time();
2279
		/** This action is documented in class.jetpack.php */
2280
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2281
		Jetpack_Options::update_options(
2282
			array(
2283
				'version'     => $new_version,
2284
				'old_version' => $jetpack_old_version,
2285
			)
2286
		);
2287
2288
		self::state( 'message', 'modules_activated' );
2289
2290
		self::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules, $redirect );
0 ignored issues
show
Documentation introduced by
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...
2291
2292
		if ( $redirect ) {
2293
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2294
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2295
				$page = $_GET['page'];
2296
			}
2297
2298
			wp_safe_redirect( self::admin_url( 'page=' . $page ) );
2299
			exit;
2300
		}
2301
	}
2302
2303
	/**
2304
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2305
	 * Make sure to tuck away module "library" files in a sub-directory.
2306
	 */
2307
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2308
		static $modules = null;
2309
2310
		if ( ! isset( $modules ) ) {
2311
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2312
			// Use the cache if we're on the front-end and it's available...
2313
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2314
				$modules = $available_modules_option[ JETPACK__VERSION ];
2315
			} else {
2316
				$files = self::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2317
2318
				$modules = array();
2319
2320
				foreach ( $files as $file ) {
2321
					if ( ! $headers = self::get_module( $file ) ) {
2322
						continue;
2323
					}
2324
2325
					$modules[ self::get_module_slug( $file ) ] = $headers['introduced'];
2326
				}
2327
2328
				Jetpack_Options::update_option(
2329
					'available_modules',
2330
					array(
2331
						JETPACK__VERSION => $modules,
2332
					)
2333
				);
2334
			}
2335
		}
2336
2337
		/**
2338
		 * Filters the array of modules available to be activated.
2339
		 *
2340
		 * @since 2.4.0
2341
		 *
2342
		 * @param array $modules Array of available modules.
2343
		 * @param string $min_version Minimum version number required to use modules.
2344
		 * @param string $max_version Maximum version number required to use modules.
2345
		 */
2346
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
0 ignored issues
show
Unused Code introduced by
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...
2347
2348
		if ( ! $min_version && ! $max_version ) {
2349
			return array_keys( $mods );
2350
		}
2351
2352
		$r = array();
2353
		foreach ( $mods as $slug => $introduced ) {
2354
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2355
				continue;
2356
			}
2357
2358
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2359
				continue;
2360
			}
2361
2362
			$r[] = $slug;
2363
		}
2364
2365
		return $r;
2366
	}
2367
2368
	/**
2369
	 * Default modules loaded on activation.
2370
	 */
2371
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2372
		$return = array();
2373
2374
		foreach ( self::get_available_modules( $min_version, $max_version ) as $module ) {
2375
			$module_data = self::get_module( $module );
2376
2377
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2378
				case 'yes':
2379
					$return[] = $module;
2380
					break;
2381
				case 'public':
2382
					if ( Jetpack_Options::get_option( 'public' ) ) {
2383
						$return[] = $module;
2384
					}
2385
					break;
2386
				case 'no':
2387
				default:
2388
					break;
2389
			}
2390
		}
2391
		/**
2392
		 * Filters the array of default modules.
2393
		 *
2394
		 * @since 2.5.0
2395
		 *
2396
		 * @param array $return Array of default modules.
2397
		 * @param string $min_version Minimum version number required to use modules.
2398
		 * @param string $max_version Maximum version number required to use modules.
2399
		 */
2400
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
0 ignored issues
show
Unused Code introduced by
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...
2401
	}
2402
2403
	/**
2404
	 * Checks activated modules during auto-activation to determine
2405
	 * if any of those modules are being deprecated.  If so, close
2406
	 * them out, and add any replacement modules.
2407
	 *
2408
	 * Runs at priority 99 by default.
2409
	 *
2410
	 * This is run late, so that it can still activate a module if
2411
	 * the new module is a replacement for another that the user
2412
	 * currently has active, even if something at the normal priority
2413
	 * would kibosh everything.
2414
	 *
2415
	 * @since 2.6
2416
	 * @uses jetpack_get_default_modules filter
2417
	 * @param array $modules
2418
	 * @return array
2419
	 */
2420
	function handle_deprecated_modules( $modules ) {
2421
		$deprecated_modules = array(
2422
			'debug'            => null,  // Closed out and moved to the debugger library.
2423
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2424
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2425
			'minileven'        => null,  // Closed out in 8.3 -- Responsive themes are common now, and so is AMP.
2426
		);
2427
2428
		// Don't activate SSO if they never completed activating WPCC.
2429
		if ( self::is_module_active( 'wpcc' ) ) {
2430
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2431
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2432
				$deprecated_modules['wpcc'] = null;
2433
			}
2434
		}
2435
2436
		foreach ( $deprecated_modules as $module => $replacement ) {
2437
			if ( self::is_module_active( $module ) ) {
2438
				self::deactivate_module( $module );
2439
				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...
2440
					$modules[] = $replacement;
2441
				}
2442
			}
2443
		}
2444
2445
		return array_unique( $modules );
2446
	}
2447
2448
	/**
2449
	 * Checks activated plugins during auto-activation to determine
2450
	 * if any of those plugins are in the list with a corresponding module
2451
	 * that is not compatible with the plugin. The module will not be allowed
2452
	 * to auto-activate.
2453
	 *
2454
	 * @since 2.6
2455
	 * @uses jetpack_get_default_modules filter
2456
	 * @param array $modules
2457
	 * @return array
2458
	 */
2459
	function filter_default_modules( $modules ) {
2460
2461
		$active_plugins = self::get_active_plugins();
2462
2463
		if ( ! empty( $active_plugins ) ) {
2464
2465
			// For each module we'd like to auto-activate...
2466
			foreach ( $modules as $key => $module ) {
2467
				// If there are potential conflicts for it...
2468
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2469
					// For each potential conflict...
2470
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2471
						// If that conflicting plugin is active...
2472
						if ( in_array( $plugin, $active_plugins ) ) {
2473
							// Remove that item from being auto-activated.
2474
							unset( $modules[ $key ] );
2475
						}
2476
					}
2477
				}
2478
			}
2479
		}
2480
2481
		return $modules;
2482
	}
2483
2484
	/**
2485
	 * Extract a module's slug from its full path.
2486
	 */
2487
	public static function get_module_slug( $file ) {
2488
		return str_replace( '.php', '', basename( $file ) );
2489
	}
2490
2491
	/**
2492
	 * Generate a module's path from its slug.
2493
	 */
2494
	public static function get_module_path( $slug ) {
2495
		/**
2496
		 * Filters the path of a modules.
2497
		 *
2498
		 * @since 7.4.0
2499
		 *
2500
		 * @param array $return The absolute path to a module's root php file
2501
		 * @param string $slug The module slug
2502
		 */
2503
		return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
0 ignored issues
show
Unused Code introduced by
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...
2504
	}
2505
2506
	/**
2507
	 * Load module data from module file. Headers differ from WordPress
2508
	 * plugin headers to avoid them being identified as standalone
2509
	 * plugins on the WordPress plugins page.
2510
	 */
2511
	public static function get_module( $module ) {
2512
		$headers = array(
2513
			'name'                      => 'Module Name',
2514
			'description'               => 'Module Description',
2515
			'sort'                      => 'Sort Order',
2516
			'recommendation_order'      => 'Recommendation Order',
2517
			'introduced'                => 'First Introduced',
2518
			'changed'                   => 'Major Changes In',
2519
			'deactivate'                => 'Deactivate',
2520
			'free'                      => 'Free',
2521
			'requires_connection'       => 'Requires Connection',
2522
			'auto_activate'             => 'Auto Activate',
2523
			'module_tags'               => 'Module Tags',
2524
			'feature'                   => 'Feature',
2525
			'additional_search_queries' => 'Additional Search Queries',
2526
			'plan_classes'              => 'Plans',
2527
		);
2528
2529
		$file = self::get_module_path( self::get_module_slug( $module ) );
2530
2531
		$mod = self::get_file_data( $file, $headers );
2532
		if ( empty( $mod['name'] ) ) {
2533
			return false;
2534
		}
2535
2536
		$mod['sort']                 = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2537
		$mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2538
		$mod['deactivate']           = empty( $mod['deactivate'] );
2539
		$mod['free']                 = empty( $mod['free'] );
2540
		$mod['requires_connection']  = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2541
2542
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2543
			$mod['auto_activate'] = 'No';
2544
		} else {
2545
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2546
		}
2547
2548
		if ( $mod['module_tags'] ) {
2549
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2550
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2551
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2552
		} else {
2553
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2554
		}
2555
2556 View Code Duplication
		if ( $mod['plan_classes'] ) {
2557
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2558
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2559
		} else {
2560
			$mod['plan_classes'] = array( 'free' );
2561
		}
2562
2563 View Code Duplication
		if ( $mod['feature'] ) {
2564
			$mod['feature'] = explode( ',', $mod['feature'] );
2565
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2566
		} else {
2567
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2568
		}
2569
2570
		/**
2571
		 * Filters the feature array on a module.
2572
		 *
2573
		 * This filter allows you to control where each module is filtered: Recommended,
2574
		 * and the default "Other" listing.
2575
		 *
2576
		 * @since 3.5.0
2577
		 *
2578
		 * @param array   $mod['feature'] The areas to feature this module:
2579
		 *     'Recommended' shows on the main Jetpack admin screen.
2580
		 *     'Other' should be the default if no other value is in the array.
2581
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2582
		 * @param array   $mod All the currently assembled module data.
2583
		 */
2584
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
0 ignored issues
show
Unused Code introduced by
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...
2585
2586
		/**
2587
		 * Filter the returned data about a module.
2588
		 *
2589
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2590
		 * so please be careful.
2591
		 *
2592
		 * @since 3.6.0
2593
		 *
2594
		 * @param array   $mod    The details of the requested module.
2595
		 * @param string  $module The slug of the module, e.g. sharedaddy
2596
		 * @param string  $file   The path to the module source file.
2597
		 */
2598
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
0 ignored issues
show
Unused Code introduced by
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...
2599
	}
2600
2601
	/**
2602
	 * Like core's get_file_data implementation, but caches the result.
2603
	 */
2604
	public static function get_file_data( $file, $headers ) {
2605
		// Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2606
		$file_name = basename( $file );
2607
2608
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2609
2610
		$file_data_option = get_transient( $cache_key );
2611
2612
		if ( ! is_array( $file_data_option ) ) {
2613
			delete_transient( $cache_key );
2614
			$file_data_option = false;
2615
		}
2616
2617
		if ( false === $file_data_option ) {
2618
			$file_data_option = array();
2619
		}
2620
2621
		$key           = md5( $file_name . serialize( $headers ) );
2622
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2623
2624
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2625
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2626
			return $file_data_option[ $key ];
2627
		}
2628
2629
		$data = get_file_data( $file, $headers );
2630
2631
		$file_data_option[ $key ] = $data;
2632
2633
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2634
2635
		return $data;
2636
	}
2637
2638
2639
	/**
2640
	 * Return translated module tag.
2641
	 *
2642
	 * @param string $tag Tag as it appears in each module heading.
2643
	 *
2644
	 * @return mixed
2645
	 */
2646
	public static function translate_module_tag( $tag ) {
2647
		return jetpack_get_module_i18n_tag( $tag );
2648
	}
2649
2650
	/**
2651
	 * Get i18n strings as a JSON-encoded string
2652
	 *
2653
	 * @return string The locale as JSON
2654
	 */
2655
	public static function get_i18n_data_json() {
2656
2657
		// WordPress 5.0 uses md5 hashes of file paths to associate translation
2658
		// JSON files with the file they should be included for. This is an md5
2659
		// of '_inc/build/admin.js'.
2660
		$path_md5 = '1bac79e646a8bf4081a5011ab72d5807';
2661
2662
		$i18n_json =
2663
				   JETPACK__PLUGIN_DIR
2664
				   . 'languages/json/jetpack-'
2665
				   . get_user_locale()
2666
				   . '-'
2667
				   . $path_md5
2668
				   . '.json';
2669
2670
		if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
2671
			$locale_data = @file_get_contents( $i18n_json );
2672
			if ( $locale_data ) {
2673
				return $locale_data;
2674
			}
2675
		}
2676
2677
		// Return valid empty Jed locale
2678
		return '{ "locale_data": { "messages": { "": {} } } }';
2679
	}
2680
2681
	/**
2682
	 * Add locale data setup to wp-i18n
2683
	 *
2684
	 * Any Jetpack script that depends on wp-i18n should use this method to set up the locale.
2685
	 *
2686
	 * The locale setup depends on an adding inline script. This is error-prone and could easily
2687
	 * result in multiple additions of the same script when exactly 0 or 1 is desireable.
2688
	 *
2689
	 * This method provides a safe way to request the setup multiple times but add the script at
2690
	 * most once.
2691
	 *
2692
	 * @since 6.7.0
2693
	 *
2694
	 * @return void
2695
	 */
2696
	public static function setup_wp_i18n_locale_data() {
2697
		static $script_added = false;
2698
		if ( ! $script_added ) {
2699
			$script_added = true;
2700
			wp_add_inline_script(
2701
				'wp-i18n',
2702
				'wp.i18n.setLocaleData( ' . self::get_i18n_data_json() . ', \'jetpack\' );'
2703
			);
2704
		}
2705
	}
2706
2707
	/**
2708
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2709
	 *
2710
	 * @since 3.9.2
2711
	 *
2712
	 * @param array $modules
2713
	 *
2714
	 * @return string|void
2715
	 */
2716
	public static function get_translated_modules( $modules ) {
2717
		foreach ( $modules as $index => $module ) {
2718
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2719
			if ( isset( $module['name'] ) ) {
2720
				$modules[ $index ]['name'] = $i18n_module['name'];
2721
			}
2722
			if ( isset( $module['description'] ) ) {
2723
				$modules[ $index ]['description']       = $i18n_module['description'];
2724
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2725
			}
2726
		}
2727
		return $modules;
2728
	}
2729
2730
	/**
2731
	 * Get a list of activated modules as an array of module slugs.
2732
	 */
2733
	public static function get_active_modules() {
2734
		$active = Jetpack_Options::get_option( 'active_modules' );
2735
2736
		if ( ! is_array( $active ) ) {
2737
			$active = array();
2738
		}
2739
2740
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2741
			$active[] = 'vaultpress';
2742
		} else {
2743
			$active = array_diff( $active, array( 'vaultpress' ) );
2744
		}
2745
2746
		// If protect is active on the main site of a multisite, it should be active on all sites.
2747
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2748
			$active[] = 'protect';
2749
		}
2750
2751
		/**
2752
		 * Allow filtering of the active modules.
2753
		 *
2754
		 * Gives theme and plugin developers the power to alter the modules that
2755
		 * are activated on the fly.
2756
		 *
2757
		 * @since 5.8.0
2758
		 *
2759
		 * @param array $active Array of active module slugs.
2760
		 */
2761
		$active = apply_filters( 'jetpack_active_modules', $active );
2762
2763
		return array_unique( $active );
2764
	}
2765
2766
	/**
2767
	 * Check whether or not a Jetpack module is active.
2768
	 *
2769
	 * @param string $module The slug of a Jetpack module.
2770
	 * @return bool
2771
	 *
2772
	 * @static
2773
	 */
2774
	public static function is_module_active( $module ) {
2775
		return in_array( $module, self::get_active_modules() );
2776
	}
2777
2778
	public static function is_module( $module ) {
2779
		return ! empty( $module ) && ! validate_file( $module, self::get_available_modules() );
2780
	}
2781
2782
	/**
2783
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2784
	 *
2785
	 * @param bool $catch True to start catching, False to stop.
2786
	 *
2787
	 * @static
2788
	 */
2789
	public static function catch_errors( $catch ) {
2790
		static $display_errors, $error_reporting;
2791
2792
		if ( $catch ) {
2793
			$display_errors  = @ini_set( 'display_errors', 1 );
2794
			$error_reporting = @error_reporting( E_ALL );
2795
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2796
		} else {
2797
			@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...
2798
			@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...
2799
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2800
		}
2801
	}
2802
2803
	/**
2804
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2805
	 */
2806
	public static function catch_errors_on_shutdown() {
2807
		self::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2808
	}
2809
2810
	/**
2811
	 * Rewrite any string to make paths easier to read.
2812
	 *
2813
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2814
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2815
	 *
2816
	 * @param $string
2817
	 * @return mixed
2818
	 */
2819
	public static function alias_directories( $string ) {
2820
		// ABSPATH has a trailing slash.
2821
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2822
		// WP_CONTENT_DIR does not have a trailing slash.
2823
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2824
2825
		return $string;
2826
	}
2827
2828
	public static function activate_default_modules(
2829
		$min_version = false,
2830
		$max_version = false,
2831
		$other_modules = array(),
2832
		$redirect = null,
2833
		$send_state_messages = null
2834
	) {
2835
		$jetpack = self::init();
2836
2837
		if ( is_null( $redirect ) ) {
2838
			if (
2839
				( defined( 'REST_REQUEST' ) && REST_REQUEST )
2840
			||
2841
				( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
2842
			||
2843
				( defined( 'WP_CLI' ) && WP_CLI )
2844
			||
2845
				( defined( 'DOING_CRON' ) && DOING_CRON )
2846
			||
2847
				( defined( 'DOING_AJAX' ) && DOING_AJAX )
2848
			) {
2849
				$redirect = false;
2850
			} elseif ( is_admin() ) {
2851
				$redirect = true;
2852
			} else {
2853
				$redirect = false;
2854
			}
2855
		}
2856
2857
		if ( is_null( $send_state_messages ) ) {
2858
			$send_state_messages = current_user_can( 'jetpack_activate_modules' );
2859
		}
2860
2861
		$modules = self::get_default_modules( $min_version, $max_version );
2862
		$modules = array_merge( $other_modules, $modules );
2863
2864
		// Look for standalone plugins and disable if active.
2865
2866
		$to_deactivate = array();
2867
		foreach ( $modules as $module ) {
2868
			if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2869
				$to_deactivate[ $module ] = $jetpack->plugins_to_deactivate[ $module ];
2870
			}
2871
		}
2872
2873
		$deactivated = array();
2874
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2875
			list( $probable_file, $probable_title ) = $deactivate_me;
2876
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2877
				$deactivated[] = $module;
2878
			}
2879
		}
2880
2881
		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...
2882
			if ( $send_state_messages ) {
2883
				self::state( 'deactivated_plugins', join( ',', $deactivated ) );
2884
			}
2885
2886
			if ( $redirect ) {
2887
				$url = add_query_arg(
2888
					array(
2889
						'action'   => 'activate_default_modules',
2890
						'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2891
					),
2892
					add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), self::admin_url( 'page=jetpack' ) )
2893
				);
2894
				wp_safe_redirect( $url );
2895
				exit;
2896
			}
2897
		}
2898
2899
		/**
2900
		 * Fires before default modules are activated.
2901
		 *
2902
		 * @since 1.9.0
2903
		 *
2904
		 * @param string $min_version Minimum version number required to use modules.
2905
		 * @param string $max_version Maximum version number required to use modules.
2906
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2907
		 */
2908
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2909
2910
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2911
		if ( $send_state_messages ) {
2912
			self::restate();
2913
			self::catch_errors( true );
2914
		}
2915
2916
		$active = self::get_active_modules();
2917
2918
		foreach ( $modules as $module ) {
2919
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2920
				$active[] = $module;
2921
				self::update_active_modules( $active );
2922
				continue;
2923
			}
2924
2925
			if ( $send_state_messages && in_array( $module, $active ) ) {
2926
				$module_info = self::get_module( $module );
2927 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2928
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2929
					if ( $active_state = self::state( $state ) ) {
2930
						$active_state = explode( ',', $active_state );
2931
					} else {
2932
						$active_state = array();
2933
					}
2934
					$active_state[] = $module;
2935
					self::state( $state, implode( ',', $active_state ) );
2936
				}
2937
				continue;
2938
			}
2939
2940
			$file = self::get_module_path( $module );
2941
			if ( ! file_exists( $file ) ) {
2942
				continue;
2943
			}
2944
2945
			// we'll override this later if the plugin can be included without fatal error
2946
			if ( $redirect ) {
2947
				wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
2948
			}
2949
2950
			if ( $send_state_messages ) {
2951
				self::state( 'error', 'module_activation_failed' );
2952
				self::state( 'module', $module );
2953
			}
2954
2955
			ob_start();
2956
			require_once $file;
2957
2958
			$active[] = $module;
2959
2960 View Code Duplication
			if ( $send_state_messages ) {
2961
2962
				$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2963
				if ( $active_state = self::state( $state ) ) {
2964
					$active_state = explode( ',', $active_state );
2965
				} else {
2966
					$active_state = array();
2967
				}
2968
				$active_state[] = $module;
2969
				self::state( $state, implode( ',', $active_state ) );
2970
			}
2971
2972
			self::update_active_modules( $active );
2973
2974
			ob_end_clean();
2975
		}
2976
2977
		if ( $send_state_messages ) {
2978
			self::state( 'error', false );
0 ignored issues
show
Documentation introduced by
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...
2979
			self::state( 'module', false );
0 ignored issues
show
Documentation introduced by
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...
2980
		}
2981
2982
		self::catch_errors( false );
2983
		/**
2984
		 * Fires when default modules are activated.
2985
		 *
2986
		 * @since 1.9.0
2987
		 *
2988
		 * @param string $min_version Minimum version number required to use modules.
2989
		 * @param string $max_version Maximum version number required to use modules.
2990
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2991
		 */
2992
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2993
	}
2994
2995
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2996
		/**
2997
		 * Fires before a module is activated.
2998
		 *
2999
		 * @since 2.6.0
3000
		 *
3001
		 * @param string $module Module slug.
3002
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
3003
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
3004
		 */
3005
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
3006
3007
		$jetpack = self::init();
3008
3009
		if ( ! strlen( $module ) ) {
3010
			return false;
3011
		}
3012
3013
		if ( ! self::is_module( $module ) ) {
3014
			return false;
3015
		}
3016
3017
		// If it's already active, then don't do it again
3018
		$active = self::get_active_modules();
3019
		foreach ( $active as $act ) {
3020
			if ( $act == $module ) {
3021
				return true;
3022
			}
3023
		}
3024
3025
		$module_data = self::get_module( $module );
3026
3027
		$is_development_mode = ( new Status() )->is_development_mode();
3028
		if ( ! self::is_active() ) {
3029
			if ( ! $is_development_mode && ! self::is_onboarding() ) {
3030
				return false;
3031
			}
3032
3033
			// If we're not connected but in development mode, make sure the module doesn't require a connection
3034
			if ( $is_development_mode && $module_data['requires_connection'] ) {
3035
				return false;
3036
			}
3037
		}
3038
3039
		// Check and see if the old plugin is active
3040
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3041
			// Deactivate the old plugin
3042
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
3043
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
3044
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
3045
				self::state( 'deactivated_plugins', $module );
3046
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
3047
				exit;
3048
			}
3049
		}
3050
3051
		// Protect won't work with mis-configured IPs
3052
		if ( 'protect' === $module ) {
3053
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
3054
			if ( ! jetpack_protect_get_ip() ) {
3055
				self::state( 'message', 'protect_misconfigured_ip' );
3056
				return false;
3057
			}
3058
		}
3059
3060
		if ( ! Jetpack_Plan::supports( $module ) ) {
3061
			return false;
3062
		}
3063
3064
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
3065
		self::state( 'module', $module );
3066
		self::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
3067
3068
		self::catch_errors( true );
3069
		ob_start();
3070
		require self::get_module_path( $module );
3071
		/** This action is documented in class.jetpack.php */
3072
		do_action( 'jetpack_activate_module', $module );
3073
		$active[] = $module;
3074
		self::update_active_modules( $active );
3075
3076
		self::state( 'error', false ); // the override
0 ignored issues
show
Documentation introduced by
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...
3077
		ob_end_clean();
3078
		self::catch_errors( false );
3079
3080
		if ( $redirect ) {
3081
			wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
3082
		}
3083
		if ( $exit ) {
3084
			exit;
3085
		}
3086
		return true;
3087
	}
3088
3089
	function activate_module_actions( $module ) {
3090
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3091
	}
3092
3093
	public static function deactivate_module( $module ) {
3094
		/**
3095
		 * Fires when a module is deactivated.
3096
		 *
3097
		 * @since 1.9.0
3098
		 *
3099
		 * @param string $module Module slug.
3100
		 */
3101
		do_action( 'jetpack_pre_deactivate_module', $module );
3102
3103
		$jetpack = self::init();
0 ignored issues
show
Unused Code introduced by
$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...
3104
3105
		$active = self::get_active_modules();
3106
		$new    = array_filter( array_diff( $active, (array) $module ) );
3107
3108
		return self::update_active_modules( $new );
3109
	}
3110
3111
	public static function enable_module_configurable( $module ) {
3112
		$module = self::get_module_slug( $module );
3113
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3114
	}
3115
3116
	/**
3117
	 * Composes a module configure URL. It uses Jetpack settings search as default value
3118
	 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3119
	 *
3120
	 * @param string $module Module slug
3121
	 * @return string $url module configuration URL
3122
	 */
3123
	public static function module_configuration_url( $module ) {
3124
		$module      = self::get_module_slug( $module );
3125
		$default_url = self::admin_url() . "#/settings?term=$module";
3126
		/**
3127
		 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3128
		 *
3129
		 * @since 6.9.0
3130
		 *
3131
		 * @param string $default_url Default url, which redirects to jetpack settings page.
3132
		 */
3133
		$url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3134
3135
		return $url;
3136
	}
3137
3138
	/* Installation */
3139
	public static function bail_on_activation( $message, $deactivate = true ) {
3140
		?>
3141
<!doctype html>
3142
<html>
3143
<head>
3144
<meta charset="<?php bloginfo( 'charset' ); ?>">
3145
<style>
3146
* {
3147
	text-align: center;
3148
	margin: 0;
3149
	padding: 0;
3150
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3151
}
3152
p {
3153
	margin-top: 1em;
3154
	font-size: 18px;
3155
}
3156
</style>
3157
<body>
3158
<p><?php echo esc_html( $message ); ?></p>
3159
</body>
3160
</html>
3161
		<?php
3162
		if ( $deactivate ) {
3163
			$plugins = get_option( 'active_plugins' );
3164
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3165
			$update  = false;
3166
			foreach ( $plugins as $i => $plugin ) {
3167
				if ( $plugin === $jetpack ) {
3168
					$plugins[ $i ] = false;
3169
					$update        = true;
3170
				}
3171
			}
3172
3173
			if ( $update ) {
3174
				update_option( 'active_plugins', array_filter( $plugins ) );
3175
			}
3176
		}
3177
		exit;
3178
	}
3179
3180
	/**
3181
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3182
	 *
3183
	 * @static
3184
	 */
3185
	public static function plugin_activation( $network_wide ) {
3186
		Jetpack_Options::update_option( 'activated', 1 );
3187
3188
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3189
			self::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3190
		}
3191
3192
		if ( $network_wide ) {
3193
			self::state( 'network_nag', true );
0 ignored issues
show
Documentation introduced by
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...
3194
		}
3195
3196
		// For firing one-off events (notices) immediately after activation
3197
		set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS );
3198
3199
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3200
3201
		Health::on_jetpack_activated();
3202
3203
		self::plugin_initialize();
3204
	}
3205
3206
	public static function get_activation_source( $referer_url ) {
3207
3208
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3209
			return array( 'wp-cli', null );
3210
		}
3211
3212
		$referer = wp_parse_url( $referer_url );
3213
3214
		$source_type  = 'unknown';
3215
		$source_query = null;
3216
3217
		if ( ! is_array( $referer ) ) {
3218
			return array( $source_type, $source_query );
3219
		}
3220
3221
		$plugins_path         = wp_parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
0 ignored issues
show
Unused Code introduced by
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...
3222
		$plugins_install_path = wp_parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
0 ignored issues
show
Unused Code introduced by
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...
3223
3224
		if ( isset( $referer['query'] ) ) {
3225
			parse_str( $referer['query'], $query_parts );
3226
		} else {
3227
			$query_parts = array();
3228
		}
3229
3230
		if ( $plugins_path === $referer['path'] ) {
3231
			$source_type = 'list';
3232
		} elseif ( $plugins_install_path === $referer['path'] ) {
3233
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3234
			switch ( $tab ) {
3235
				case 'popular':
3236
					$source_type = 'popular';
3237
					break;
3238
				case 'recommended':
3239
					$source_type = 'recommended';
3240
					break;
3241
				case 'favorites':
3242
					$source_type = 'favorites';
3243
					break;
3244
				case 'search':
3245
					$source_type  = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3246
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3247
					break;
3248
				default:
3249
					$source_type = 'featured';
3250
			}
3251
		}
3252
3253
		return array( $source_type, $source_query );
3254
	}
3255
3256
	/**
3257
	 * Runs before bumping version numbers up to a new version
3258
	 *
3259
	 * @param string $version    Version:timestamp.
3260
	 * @param string $old_version Old Version:timestamp or false if not set yet.
3261
	 */
3262
	public static function do_version_bump( $version, $old_version ) {
3263
		if ( $old_version ) { // For existing Jetpack installations.
3264
3265
			// If a front end page is visited after the update, the 'wp' action will fire.
3266
			add_action( 'wp', 'Jetpack::set_update_modal_display' );
3267
3268
			// If an admin page is visited after the update, the 'current_screen' action will fire.
3269
			add_action( 'current_screen', 'Jetpack::set_update_modal_display' );
3270
		}
3271
	}
3272
3273
	/**
3274
	 * Sets the display_update_modal state.
3275
	 */
3276
	public static function set_update_modal_display() {
3277
		self::state( 'display_update_modal', true );
0 ignored issues
show
Documentation introduced by
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...
3278
	}
3279
3280
	/**
3281
	 * Sets the internal version number and activation state.
3282
	 *
3283
	 * @static
3284
	 */
3285
	public static function plugin_initialize() {
3286
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3287
			Jetpack_Options::update_option( 'activated', 2 );
3288
		}
3289
3290 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3291
			$version = $old_version = JETPACK__VERSION . ':' . time();
3292
			/** This action is documented in class.jetpack.php */
3293
			do_action( 'updating_jetpack_version', $version, false );
3294
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3295
		}
3296
3297
		self::load_modules();
3298
3299
		Jetpack_Options::delete_option( 'do_activate' );
3300
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3301
	}
3302
3303
	/**
3304
	 * Removes all connection options
3305
	 *
3306
	 * @static
3307
	 */
3308
	public static function plugin_deactivation() {
3309
		require_once ABSPATH . '/wp-admin/includes/plugin.php';
3310
		$tracking = new Tracking();
3311
		$tracking->record_user_event( 'deactivate_plugin', array() );
3312
		if ( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3313
			Jetpack_Network::init()->deactivate();
3314
		} else {
3315
			self::disconnect( false );
3316
			// Jetpack_Heartbeat::init()->deactivate();
3317
		}
3318
	}
3319
3320
	/**
3321
	 * Disconnects from the Jetpack servers.
3322
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3323
	 *
3324
	 * @static
3325
	 */
3326
	public static function disconnect( $update_activated_state = true ) {
3327
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3328
		$connection = self::connection();
3329
		$connection->clean_nonces( true );
3330
3331
		// If the site is in an IDC because sync is not allowed,
3332
		// let's make sure to not disconnect the production site.
3333
		if ( ! self::validate_sync_error_idc_option() ) {
3334
			$tracking = new Tracking();
3335
			$tracking->record_user_event( 'disconnect_site', array() );
3336
3337
			$connection->disconnect_site_wpcom( true );
3338
		}
3339
3340
		$connection->delete_all_connection_tokens( true );
3341
		Jetpack_IDC::clear_all_idc_options();
3342
3343
		if ( $update_activated_state ) {
3344
			Jetpack_Options::update_option( 'activated', 4 );
3345
		}
3346
3347
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3348
			// Check then record unique disconnection if site has never been disconnected previously
3349
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3350
				$jetpack_unique_connection['disconnected'] = 1;
3351
			} else {
3352
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3353
					// track unique disconnect
3354
					$jetpack = self::init();
3355
3356
					$jetpack->stat( 'connections', 'unique-disconnect' );
3357
					$jetpack->do_stats( 'server_side' );
3358
				}
3359
				// increment number of times disconnected
3360
				$jetpack_unique_connection['disconnected'] += 1;
3361
			}
3362
3363
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3364
		}
3365
3366
		// Delete all the sync related data. Since it could be taking up space.
3367
		Sender::get_instance()->uninstall();
3368
3369
		// Disable the Heartbeat cron
3370
		Jetpack_Heartbeat::init()->deactivate();
3371
	}
3372
3373
	/**
3374
	 * Unlinks the current user from the linked WordPress.com user.
3375
	 *
3376
	 * @deprecated since 7.7
3377
	 * @see Automattic\Jetpack\Connection\Manager::disconnect_user()
3378
	 *
3379
	 * @param Integer $user_id the user identifier.
0 ignored issues
show
Documentation introduced by
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...
3380
	 * @return Boolean Whether the disconnection of the user was successful.
3381
	 */
3382
	public static function unlink_user( $user_id = null ) {
3383
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::disconnect_user' );
3384
		return Connection_Manager::disconnect_user( $user_id );
3385
	}
3386
3387
	/**
3388
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3389
	 */
3390
	public static function try_registration() {
3391
		$terms_of_service = new Terms_Of_Service();
3392
		// The user has agreed to the TOS at some point by now.
3393
		$terms_of_service->agree();
3394
3395
		// Let's get some testing in beta versions and such.
3396
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3397
			// Before attempting to connect, let's make sure that the domains are viable.
3398
			$domains_to_check = array_unique(
3399
				array(
3400
					'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
0 ignored issues
show
Unused Code introduced by
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...
3401
					'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ),
0 ignored issues
show
Unused Code introduced by
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...
3402
				)
3403
			);
3404
			foreach ( $domains_to_check as $domain ) {
3405
				$result = self::connection()->is_usable_domain( $domain );
0 ignored issues
show
Documentation introduced by
$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...
3406
				if ( is_wp_error( $result ) ) {
3407
					return $result;
3408
				}
3409
			}
3410
		}
3411
3412
		$result = self::register();
3413
3414
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3415
		if ( ! $result || is_wp_error( $result ) ) {
3416
			return $result;
3417
		} else {
3418
			return true;
3419
		}
3420
	}
3421
3422
	/**
3423
	 * Tracking an internal event log. Try not to put too much chaff in here.
3424
	 *
3425
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3426
	 */
3427
	public static function log( $code, $data = null ) {
3428
		// only grab the latest 200 entries
3429
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3430
3431
		// Append our event to the log
3432
		$log_entry = array(
3433
			'time'    => time(),
3434
			'user_id' => get_current_user_id(),
3435
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3436
			'code'    => $code,
3437
		);
3438
		// Don't bother storing it unless we've got some.
3439
		if ( ! is_null( $data ) ) {
3440
			$log_entry['data'] = $data;
3441
		}
3442
		$log[] = $log_entry;
3443
3444
		// Try add_option first, to make sure it's not autoloaded.
3445
		// @todo: Add an add_option method to Jetpack_Options
3446
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3447
			Jetpack_Options::update_option( 'log', $log );
3448
		}
3449
3450
		/**
3451
		 * Fires when Jetpack logs an internal event.
3452
		 *
3453
		 * @since 3.0.0
3454
		 *
3455
		 * @param array $log_entry {
3456
		 *  Array of details about the log entry.
3457
		 *
3458
		 *  @param string time Time of the event.
3459
		 *  @param int user_id ID of the user who trigerred the event.
3460
		 *  @param int blog_id Jetpack Blog ID.
3461
		 *  @param string code Unique name for the event.
3462
		 *  @param string data Data about the event.
3463
		 * }
3464
		 */
3465
		do_action( 'jetpack_log_entry', $log_entry );
3466
	}
3467
3468
	/**
3469
	 * Get the internal event log.
3470
	 *
3471
	 * @param $event (string) - only return the specific log events
3472
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3473
	 *
3474
	 * @return array of log events || WP_Error for invalid params
3475
	 */
3476
	public static function get_log( $event = false, $num = false ) {
3477
		if ( $event && ! is_string( $event ) ) {
3478
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
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...
3479
		}
3480
3481
		if ( $num && ! is_numeric( $num ) ) {
3482
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
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...
3483
		}
3484
3485
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3486
3487
		// If nothing set - act as it did before, otherwise let's start customizing the output
3488
		if ( ! $num && ! $event ) {
3489
			return $entire_log;
3490
		} else {
3491
			$entire_log = array_reverse( $entire_log );
3492
		}
3493
3494
		$custom_log_output = array();
3495
3496
		if ( $event ) {
3497
			foreach ( $entire_log as $log_event ) {
3498
				if ( $event == $log_event['code'] ) {
3499
					$custom_log_output[] = $log_event;
3500
				}
3501
			}
3502
		} else {
3503
			$custom_log_output = $entire_log;
3504
		}
3505
3506
		if ( $num ) {
3507
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3508
		}
3509
3510
		return $custom_log_output;
3511
	}
3512
3513
	/**
3514
	 * Log modification of important settings.
3515
	 */
3516
	public static function log_settings_change( $option, $old_value, $value ) {
3517
		switch ( $option ) {
3518
			case 'jetpack_sync_non_public_post_stati':
3519
				self::log( $option, $value );
3520
				break;
3521
		}
3522
	}
3523
3524
	/**
3525
	 * Return stat data for WPCOM sync
3526
	 */
3527
	public static function get_stat_data( $encode = true, $extended = true ) {
3528
		$data = Jetpack_Heartbeat::generate_stats_array();
3529
3530
		if ( $extended ) {
3531
			$additional_data = self::get_additional_stat_data();
3532
			$data            = array_merge( $data, $additional_data );
3533
		}
3534
3535
		if ( $encode ) {
3536
			return json_encode( $data );
3537
		}
3538
3539
		return $data;
3540
	}
3541
3542
	/**
3543
	 * Get additional stat data to sync to WPCOM
3544
	 */
3545
	public static function get_additional_stat_data( $prefix = '' ) {
3546
		$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...
3547
		$return[ "{$prefix}plugins-extra" ] = self::get_parsed_plugin_data();
3548
		$return[ "{$prefix}users" ]         = (int) self::get_site_user_count();
3549
		$return[ "{$prefix}site-count" ]    = 0;
3550
3551
		if ( function_exists( 'get_blog_count' ) ) {
3552
			$return[ "{$prefix}site-count" ] = get_blog_count();
3553
		}
3554
		return $return;
3555
	}
3556
3557
	private static function get_site_user_count() {
3558
		global $wpdb;
3559
3560
		if ( function_exists( 'wp_is_large_network' ) ) {
3561
			if ( wp_is_large_network( 'users' ) ) {
3562
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3563
			}
3564
		}
3565
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3566
			// It wasn't there, so regenerate the data and save the transient
3567
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3568
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3569
		}
3570
		return $user_count;
3571
	}
3572
3573
	/* Admin Pages */
3574
3575
	function admin_init() {
3576
		// If the plugin is not connected, display a connect message.
3577
		if (
3578
			// the plugin was auto-activated and needs its candy
3579
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3580
		||
3581
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3582
			! Jetpack_Options::get_option( 'activated' )
3583
		) {
3584
			self::plugin_initialize();
3585
		}
3586
3587
		$is_development_mode = ( new Status() )->is_development_mode();
3588
		if ( ! self::is_active() && ! $is_development_mode ) {
3589
			Jetpack_Connection_Banner::init();
3590
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3591
			// Upgrade: 1.1 -> 1.1.1
3592
			// Check and see if host can verify the Jetpack servers' SSL certificate
3593
			$args       = array();
3594
			$connection = self::connection();
3595
			Client::_wp_remote_request(
3596
				Connection_Utils::fix_url_for_bad_hosts( $connection->api_url( 'test' ) ),
3597
				$args,
3598
				true
3599
			);
3600
		}
3601
3602
		Jetpack_Wizard_Banner::init();
3603
3604
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3605
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3606
		}
3607
3608
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3609
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3610
		add_action( 'admin_enqueue_scripts', array( $this, 'deactivate_dialog' ) );
3611
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3612
3613
		if ( self::is_active() || $is_development_mode ) {
3614
			// Artificially throw errors in certain specific cases during plugin activation.
3615
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3616
		}
3617
3618
		// Add custom column in wp-admin/users.php to show whether user is linked.
3619
		add_filter( 'manage_users_columns', array( $this, 'jetpack_icon_user_connected' ) );
3620
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3621
		add_action( 'admin_print_styles', array( $this, 'jetpack_user_col_style' ) );
3622
	}
3623
3624
	function admin_body_class( $admin_body_class = '' ) {
3625
		$classes = explode( ' ', trim( $admin_body_class ) );
3626
3627
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3628
3629
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3630
		return " $admin_body_class ";
3631
	}
3632
3633
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3634
		return $admin_body_class . ' jetpack-pagestyles ';
3635
	}
3636
3637
	/**
3638
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3639
	 * This function artificially throws errors for such cases (per a specific list).
3640
	 *
3641
	 * @param string $plugin The activated plugin.
3642
	 */
3643
	function throw_error_on_activate_plugin( $plugin ) {
3644
		$active_modules = self::get_active_modules();
3645
3646
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3647
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3648
			$throw = false;
3649
3650
			// Try and make sure it really was the stats plugin
3651
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3652
				if ( 'stats.php' == basename( $plugin ) ) {
3653
					$throw = true;
3654
				}
3655
			} else {
3656
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3657
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3658
					$throw = true;
3659
				}
3660
			}
3661
3662
			if ( $throw ) {
3663
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3664
			}
3665
		}
3666
	}
3667
3668
	function intercept_plugin_error_scrape_init() {
3669
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3670
	}
3671
3672
	function intercept_plugin_error_scrape( $action, $result ) {
3673
		if ( ! $result ) {
3674
			return;
3675
		}
3676
3677
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3678
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3679
				self::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3680
			}
3681
		}
3682
	}
3683
3684
	/**
3685
	 * Register the remote file upload request handlers, if needed.
3686
	 *
3687
	 * @access public
3688
	 */
3689
	public function add_remote_request_handlers() {
3690
		// Remote file uploads are allowed only via AJAX requests.
3691
		if ( ! is_admin() || ! Constants::get_constant( 'DOING_AJAX' ) ) {
3692
			return;
3693
		}
3694
3695
		// Remote file uploads are allowed only for a set of specific AJAX actions.
3696
		$remote_request_actions = array(
3697
			'jetpack_upload_file',
3698
			'jetpack_update_file',
3699
		);
3700
3701
		// phpcs:ignore WordPress.Security.NonceVerification
3702
		if ( ! isset( $_POST['action'] ) || ! in_array( $_POST['action'], $remote_request_actions, true ) ) {
3703
			return;
3704
		}
3705
3706
		// Require Jetpack authentication for the remote file upload AJAX requests.
3707
		if ( ! $this->connection_manager ) {
3708
			$this->connection_manager = new Connection_Manager();
3709
		}
3710
3711
		$this->connection_manager->require_jetpack_authentication();
3712
3713
		// Register the remote file upload AJAX handlers.
3714
		foreach ( $remote_request_actions as $action ) {
3715
			add_action( "wp_ajax_nopriv_{$action}", array( $this, 'remote_request_handlers' ) );
3716
		}
3717
	}
3718
3719
	/**
3720
	 * Handler for Jetpack remote file uploads.
3721
	 *
3722
	 * @access public
3723
	 */
3724
	public function remote_request_handlers() {
3725
		$action = current_filter();
0 ignored issues
show
Unused Code introduced by
$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...
3726
3727
		switch ( current_filter() ) {
3728
			case 'wp_ajax_nopriv_jetpack_upload_file':
3729
				$response = $this->upload_handler();
3730
				break;
3731
3732
			case 'wp_ajax_nopriv_jetpack_update_file':
3733
				$response = $this->upload_handler( true );
3734
				break;
3735
			default:
3736
				$response = new WP_Error( 'unknown_handler', 'Unknown Handler', 400 );
0 ignored issues
show
Unused Code introduced by
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...
3737
				break;
3738
		}
3739
3740
		if ( ! $response ) {
3741
			$response = new WP_Error( 'unknown_error', 'Unknown Error', 400 );
0 ignored issues
show
Unused Code introduced by
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...
3742
		}
3743
3744
		if ( is_wp_error( $response ) ) {
3745
			$status_code       = $response->get_error_data();
0 ignored issues
show
Bug introduced by
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...
3746
			$error             = $response->get_error_code();
0 ignored issues
show
Bug introduced by
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...
3747
			$error_description = $response->get_error_message();
0 ignored issues
show
Bug introduced by
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...
3748
3749
			if ( ! is_int( $status_code ) ) {
3750
				$status_code = 400;
3751
			}
3752
3753
			status_header( $status_code );
3754
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3755
		}
3756
3757
		status_header( 200 );
3758
		if ( true === $response ) {
3759
			exit;
3760
		}
3761
3762
		die( json_encode( (object) $response ) );
3763
	}
3764
3765
	/**
3766
	 * Uploads a file gotten from the global $_FILES.
3767
	 * If `$update_media_item` is true and `post_id` is defined
3768
	 * the attachment file of the media item (gotten through of the post_id)
3769
	 * will be updated instead of add a new one.
3770
	 *
3771
	 * @param  boolean $update_media_item - update media attachment
3772
	 * @return array - An array describing the uploadind files process
3773
	 */
3774
	function upload_handler( $update_media_item = false ) {
3775
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3776
			return new WP_Error( 405, get_status_header_desc( 405 ), 405 );
0 ignored issues
show
Unused Code introduced by
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...
3777
		}
3778
3779
		$user = wp_authenticate( '', '' );
3780
		if ( ! $user || is_wp_error( $user ) ) {
3781
			return new WP_Error( 403, get_status_header_desc( 403 ), 403 );
0 ignored issues
show
Unused Code introduced by
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...
3782
		}
3783
3784
		wp_set_current_user( $user->ID );
3785
3786
		if ( ! current_user_can( 'upload_files' ) ) {
3787
			return new WP_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
0 ignored issues
show
Unused Code introduced by
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...
3788
		}
3789
3790
		if ( empty( $_FILES ) ) {
3791
			return new WP_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
0 ignored issues
show
Unused Code introduced by
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...
3792
		}
3793
3794
		foreach ( array_keys( $_FILES ) as $files_key ) {
3795
			if ( ! isset( $_POST[ "_jetpack_file_hmac_{$files_key}" ] ) ) {
3796
				return new WP_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
0 ignored issues
show
Unused Code introduced by
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...
3797
			}
3798
		}
3799
3800
		$media_keys = array_keys( $_FILES['media'] );
3801
3802
		$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...
3803
		if ( ! $token || is_wp_error( $token ) ) {
3804
			return new WP_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
0 ignored issues
show
Unused Code introduced by
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...
3805
		}
3806
3807
		$uploaded_files = array();
3808
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3809
		unset( $GLOBALS['post'] );
3810
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3811
			$file = array();
3812
			foreach ( $media_keys as $media_key ) {
3813
				$file[ $media_key ] = $_FILES['media'][ $media_key ][ $index ];
3814
			}
3815
3816
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][ $index ] );
3817
3818
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3819
			if ( $hmac_provided !== $hmac_file ) {
3820
				$uploaded_files[ $index ] = (object) array(
3821
					'error'             => 'invalid_hmac',
3822
					'error_description' => 'The corresponding HMAC for this file does not match',
3823
				);
3824
				continue;
3825
			}
3826
3827
			$_FILES['.jetpack.upload.'] = $file;
3828
			$post_id                    = isset( $_POST['post_id'][ $index ] ) ? absint( $_POST['post_id'][ $index ] ) : 0;
3829
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3830
				$post_id = 0;
3831
			}
3832
3833
			if ( $update_media_item ) {
3834
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3835
					return new WP_Error( 'invalid_input', 'Media ID must be defined.', 400 );
0 ignored issues
show
Unused Code introduced by
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...
3836
				}
3837
3838
				$media_array = $_FILES['media'];
3839
3840
				$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...
3841
				$file_array['type']     = $media_array['type'][0];
3842
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3843
				$file_array['error']    = $media_array['error'][0];
3844
				$file_array['size']     = $media_array['size'][0];
3845
3846
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3847
3848
				if ( is_wp_error( $edited_media_item ) ) {
3849
					return $edited_media_item;
3850
				}
3851
3852
				$response = (object) array(
3853
					'id'   => (string) $post_id,
3854
					'file' => (string) $edited_media_item->post_title,
3855
					'url'  => (string) wp_get_attachment_url( $post_id ),
3856
					'type' => (string) $edited_media_item->post_mime_type,
3857
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3858
				);
3859
3860
				return (array) array( $response );
3861
			}
3862
3863
			$attachment_id = media_handle_upload(
3864
				'.jetpack.upload.',
3865
				$post_id,
3866
				array(),
3867
				array(
3868
					'action' => 'jetpack_upload_file',
3869
				)
3870
			);
3871
3872
			if ( ! $attachment_id ) {
3873
				$uploaded_files[ $index ] = (object) array(
3874
					'error'             => 'unknown',
3875
					'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site',
3876
				);
3877
			} elseif ( is_wp_error( $attachment_id ) ) {
3878
				$uploaded_files[ $index ] = (object) array(
3879
					'error'             => 'attachment_' . $attachment_id->get_error_code(),
3880
					'error_description' => $attachment_id->get_error_message(),
3881
				);
3882
			} else {
3883
				$attachment               = get_post( $attachment_id );
3884
				$uploaded_files[ $index ] = (object) array(
3885
					'id'   => (string) $attachment_id,
3886
					'file' => $attachment->post_title,
3887
					'url'  => wp_get_attachment_url( $attachment_id ),
3888
					'type' => $attachment->post_mime_type,
3889
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3890
				);
3891
				// Zip files uploads are not supported unless they are done for installation purposed
3892
				// lets delete them in case something goes wrong in this whole process
3893
				if ( 'application/zip' === $attachment->post_mime_type ) {
3894
					// Schedule a cleanup for 2 hours from now in case of failed install.
3895
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3896
				}
3897
			}
3898
		}
3899
		if ( ! is_null( $global_post ) ) {
3900
			$GLOBALS['post'] = $global_post;
3901
		}
3902
3903
		return $uploaded_files;
3904
	}
3905
3906
	/**
3907
	 * Add help to the Jetpack page
3908
	 *
3909
	 * @since Jetpack (1.2.3)
3910
	 * @return false if not the Jetpack page
3911
	 */
3912
	function admin_help() {
3913
		$current_screen = get_current_screen();
3914
3915
		// Overview
3916
		$current_screen->add_help_tab(
3917
			array(
3918
				'id'      => 'home',
3919
				'title'   => __( 'Home', 'jetpack' ),
3920
				'content' =>
3921
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3922
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3923
					'<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>',
3924
			)
3925
		);
3926
3927
		// Screen Content
3928
		if ( current_user_can( 'manage_options' ) ) {
3929
			$current_screen->add_help_tab(
3930
				array(
3931
					'id'      => 'settings',
3932
					'title'   => __( 'Settings', 'jetpack' ),
3933
					'content' =>
3934
						'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3935
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3936
						'<ol>' .
3937
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.', 'jetpack' ) . '</li>' .
3938
							'<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>' .
3939
						'</ol>' .
3940
						'<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>',
3941
				)
3942
			);
3943
		}
3944
3945
		// Help Sidebar
3946
		$support_url = Redirect::get_url( 'jetpack-support' );
3947
		$faq_url     = Redirect::get_url( 'jetpack-faq' );
3948
		$current_screen->set_help_sidebar(
3949
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3950
			'<p><a href="' . $faq_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack FAQ', 'jetpack' ) . '</a></p>' .
3951
			'<p><a href="' . $support_url . '" rel="noopener noreferrer" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3952
			'<p><a href="' . self::admin_url( array( 'page' => 'jetpack-debugger' ) ) . '">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3953
		);
3954
	}
3955
3956
	function admin_menu_css() {
3957
		wp_enqueue_style( 'jetpack-icons' );
3958
	}
3959
3960
	function admin_menu_order() {
3961
		return true;
3962
	}
3963
3964 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3965
		$jp_menu_order = array();
3966
3967
		foreach ( $menu_order as $index => $item ) {
3968
			if ( $item != 'jetpack' ) {
3969
				$jp_menu_order[] = $item;
3970
			}
3971
3972
			if ( $index == 0 ) {
3973
				$jp_menu_order[] = 'jetpack';
3974
			}
3975
		}
3976
3977
		return $jp_menu_order;
3978
	}
3979
3980
	function admin_banner_styles() {
3981
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3982
3983 View Code Duplication
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3984
			wp_register_style(
3985
				'jetpack-dops-style',
3986
				plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3987
				array(),
3988
				JETPACK__VERSION
3989
			);
3990
		}
3991
3992
		wp_enqueue_style(
3993
			'jetpack',
3994
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3995
			array( 'jetpack-dops-style' ),
3996
			JETPACK__VERSION . '-20121016'
3997
		);
3998
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3999
		wp_style_add_data( 'jetpack', 'suffix', $min );
4000
	}
4001
4002
	function plugin_action_links( $actions ) {
4003
4004
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack' ), 'Jetpack' ) );
4005
4006
		if ( current_user_can( 'jetpack_manage_modules' ) && ( self::is_active() || ( new Status() )->is_development_mode() ) ) {
4007
			return array_merge(
4008
				$jetpack_home,
4009
				array( 'settings' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
4010
				array( 'support' => sprintf( '<a href="%s">%s</a>', self::admin_url( 'page=jetpack-debugger ' ), __( 'Support', 'jetpack' ) ) ),
4011
				$actions
4012
			);
4013
		}
4014
4015
		return array_merge( $jetpack_home, $actions );
4016
	}
4017
4018
	/**
4019
	 * Adds the deactivation warning modal if there are other active plugins using the connection
4020
	 *
4021
	 * @param string $hook The current admin page.
4022
	 *
4023
	 * @return void
4024
	 */
4025
	public function deactivate_dialog( $hook ) {
4026
		if (
4027
			'plugins.php' === $hook
4028
			&& self::is_active()
4029
		) {
4030
4031
			$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4032
4033
			if ( count( $active_plugins_using_connection ) > 1 ) {
4034
4035
				add_thickbox();
4036
4037
				wp_register_script(
4038
					'jp-tracks',
4039
					'//stats.wp.com/w.js',
4040
					array(),
4041
					gmdate( 'YW' ),
4042
					true
4043
				);
4044
4045
				wp_register_script(
4046
					'jp-tracks-functions',
4047
					plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ),
4048
					array( 'jp-tracks' ),
4049
					JETPACK__VERSION,
4050
					false
4051
				);
4052
4053
				wp_enqueue_script(
4054
					'jetpack-deactivate-dialog-js',
4055
					Assets::get_file_url_for_environment(
4056
						'_inc/build/jetpack-deactivate-dialog.min.js',
4057
						'_inc/jetpack-deactivate-dialog.js'
4058
					),
4059
					array( 'jquery', 'jp-tracks-functions' ),
4060
					JETPACK__VERSION,
4061
					true
4062
				);
4063
4064
				wp_localize_script(
4065
					'jetpack-deactivate-dialog-js',
4066
					'deactivate_dialog',
4067
					array(
4068
						'title'            => __( 'Deactivate Jetpack', 'jetpack' ),
4069
						'deactivate_label' => __( 'Disconnect and Deactivate', 'jetpack' ),
4070
						'tracksUserData'   => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
4071
					)
4072
				);
4073
4074
				add_action( 'admin_footer', array( $this, 'deactivate_dialog_content' ) );
4075
4076
				wp_enqueue_style( 'jetpack-deactivate-dialog', plugins_url( 'css/jetpack-deactivate-dialog.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
4077
			}
4078
		}
4079
	}
4080
4081
	/**
4082
	 * Outputs the content of the deactivation modal
4083
	 *
4084
	 * @return void
4085
	 */
4086
	public function deactivate_dialog_content() {
4087
		$active_plugins_using_connection = Connection_Plugin_Storage::get_all();
4088
		unset( $active_plugins_using_connection['jetpack'] );
4089
		$this->load_view( 'admin/deactivation-dialog.php', $active_plugins_using_connection );
0 ignored issues
show
Bug introduced by
It seems like $active_plugins_using_connection defined by \Automattic\Jetpack\Conn...ugin_Storage::get_all() on line 4087 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...
4090
	}
4091
4092
	/**
4093
	 * Filters the login URL to include the registration flow in case the user isn't logged in.
4094
	 *
4095
	 * @param string $login_url The wp-login URL.
4096
	 * @param string $redirect  URL to redirect users after logging in.
4097
	 * @since Jetpack 8.4
4098
	 * @return string
4099
	 */
4100
	public function login_url( $login_url, $redirect ) {
4101
		parse_str( wp_parse_url( $redirect, PHP_URL_QUERY ), $redirect_parts );
0 ignored issues
show
Unused Code introduced by
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...
4102
		if ( ! empty( $redirect_parts[ self::$jetpack_redirect_login ] ) ) {
4103
			$login_url = add_query_arg( self::$jetpack_redirect_login, 'true', $login_url );
4104
		}
4105
		return $login_url;
4106
	}
4107
4108
	/**
4109
	 * Redirects non-authenticated users to authenticate with Calypso if redirect flag is set.
4110
	 *
4111
	 * @since Jetpack 8.4
4112
	 */
4113
	public function login_init() {
4114
		// phpcs:ignore WordPress.Security.NonceVerification
4115
		if ( ! empty( $_GET[ self::$jetpack_redirect_login ] ) ) {
4116
			add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4117
			wp_safe_redirect(
4118
				add_query_arg(
4119
					array(
4120
						'forceInstall' => 1,
4121
						'url'          => rawurlencode( get_site_url() ),
4122
					),
4123
					// @todo provide way to go to specific calypso env.
4124
					self::get_calypso_host() . 'jetpack/connect'
4125
				)
4126
			);
4127
			exit;
4128
		}
4129
	}
4130
4131
	/*
4132
	 * Registration flow:
4133
	 * 1 - ::admin_page_load() action=register
4134
	 * 2 - ::try_registration()
4135
	 * 3 - ::register()
4136
	 *     - Creates jetpack_register option containing two secrets and a timestamp
4137
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4138
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4139
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
4140
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4141
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4142
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4143
	 *       jetpack_id, jetpack_secret, jetpack_public
4144
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4145
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4146
	 * 5 - user logs in with WP.com account
4147
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4148
	 *		- Manager::authorize()
4149
	 *		- Manager::get_token()
4150
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
4151
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4152
	 *			- which responds with access_token, token_type, scope
4153
	 *		- Manager::authorize() stores jetpack_options: user_token => access_token.$user_id
4154
	 *		- Jetpack::activate_default_modules()
4155
	 *     		- Deactivates deprecated plugins
4156
	 *     		- Activates all default modules
4157
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4158
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4159
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4160
	 *     Done!
4161
	 */
4162
4163
	/**
4164
	 * Handles the page load events for the Jetpack admin page
4165
	 */
4166
	function admin_page_load() {
4167
		$error = false;
4168
4169
		// Make sure we have the right body class to hook stylings for subpages off of.
4170
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ), 20 );
4171
4172
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
4173
			// Should only be used in intermediate redirects to preserve state across redirects
4174
			self::restate();
4175
		}
4176
4177
		if ( isset( $_GET['connect_url_redirect'] ) ) {
4178
			// @todo: Add validation against a known allowed list.
4179
			$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4180
			// User clicked in the iframe to link their accounts
4181
			if ( ! self::is_user_connected() ) {
4182
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4183
4184
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4185
				$connect_url = $this->build_connect_url( true, $redirect, $from );
4186
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4187
4188
				if ( isset( $_GET['notes_iframe'] ) ) {
4189
					$connect_url .= '&notes_iframe';
4190
				}
4191
				wp_redirect( $connect_url );
4192
				exit;
4193
			} else {
4194
				if ( ! isset( $_GET['calypso_env'] ) ) {
4195
					self::state( 'message', 'already_authorized' );
4196
					wp_safe_redirect( self::admin_url() );
4197
					exit;
4198
				} else {
4199
					$connect_url  = $this->build_connect_url( true, false, $from );
4200
					$connect_url .= '&already_authorized=true';
4201
					wp_redirect( $connect_url );
4202
					exit;
4203
				}
4204
			}
4205
		}
4206
4207
		if ( isset( $_GET['action'] ) ) {
4208
			switch ( $_GET['action'] ) {
4209
				case 'authorize':
4210
					if ( self::is_active() && self::is_user_connected() ) {
4211
						self::state( 'message', 'already_authorized' );
4212
						wp_safe_redirect( self::admin_url() );
4213
						exit;
4214
					}
4215
					self::log( 'authorize' );
4216
					$client_server = new Jetpack_Client_Server();
4217
					$client_server->client_authorize();
4218
					exit;
4219
				case 'register':
4220
					if ( ! current_user_can( 'jetpack_connect' ) ) {
4221
						$error = 'cheatin';
4222
						break;
4223
					}
4224
					check_admin_referer( 'jetpack-register' );
4225
					self::log( 'register' );
4226
					self::maybe_set_version_option();
4227
					$registered = self::try_registration();
4228 View Code Duplication
					if ( is_wp_error( $registered ) ) {
4229
						$error = $registered->get_error_code();
0 ignored issues
show
Bug introduced by
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...
4230
						self::state( 'error', $error );
4231
						self::state( 'error', $registered->get_error_message() );
0 ignored issues
show
Bug introduced by
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...
4232
4233
						/**
4234
						 * Jetpack registration Error.
4235
						 *
4236
						 * @since 7.5.0
4237
						 *
4238
						 * @param string|int $error The error code.
4239
						 * @param \WP_Error $registered The error object.
4240
						 */
4241
						do_action( 'jetpack_connection_register_fail', $error, $registered );
4242
						break;
4243
					}
4244
4245
					$from     = isset( $_GET['from'] ) ? $_GET['from'] : false;
4246
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4247
4248
					/**
4249
					 * Jetpack registration Success.
4250
					 *
4251
					 * @since 7.5.0
4252
					 *
4253
					 * @param string $from 'from' GET parameter;
4254
					 */
4255
					do_action( 'jetpack_connection_register_success', $from );
4256
4257
					$url = $this->build_connect_url( true, $redirect, $from );
4258
4259
					if ( ! empty( $_GET['onboarding'] ) ) {
4260
						$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4261
					}
4262
4263
					if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4264
						$url = add_query_arg( 'auth_approved', 'true', $url );
4265
					}
4266
4267
					wp_redirect( $url );
4268
					exit;
4269
				case 'activate':
4270
					if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4271
						$error = 'cheatin';
4272
						break;
4273
					}
4274
4275
					$module = stripslashes( $_GET['module'] );
4276
					check_admin_referer( "jetpack_activate-$module" );
4277
					self::log( 'activate', $module );
4278
					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...
4279
						self::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4280
					}
4281
					// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4282
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4283
					exit;
4284
				case 'activate_default_modules':
4285
					check_admin_referer( 'activate_default_modules' );
4286
					self::log( 'activate_default_modules' );
4287
					self::restate();
4288
					$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4289
					$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4290
					$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4291
					self::activate_default_modules( $min_version, $max_version, $other_modules );
4292
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4293
					exit;
4294
				case 'disconnect':
4295
					if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4296
						$error = 'cheatin';
4297
						break;
4298
					}
4299
4300
					check_admin_referer( 'jetpack-disconnect' );
4301
					self::log( 'disconnect' );
4302
					self::disconnect();
4303
					wp_safe_redirect( self::admin_url( 'disconnected=true' ) );
4304
					exit;
4305
				case 'reconnect':
4306
					if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4307
						$error = 'cheatin';
4308
						break;
4309
					}
4310
4311
					check_admin_referer( 'jetpack-reconnect' );
4312
					self::log( 'reconnect' );
4313
					$this->disconnect();
4314
					wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4315
					exit;
4316 View Code Duplication
				case 'deactivate':
4317
					if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4318
						$error = 'cheatin';
4319
						break;
4320
					}
4321
4322
					$modules = stripslashes( $_GET['module'] );
4323
					check_admin_referer( "jetpack_deactivate-$modules" );
4324
					foreach ( explode( ',', $modules ) as $module ) {
4325
						self::log( 'deactivate', $module );
4326
						self::deactivate_module( $module );
4327
						self::state( 'message', 'module_deactivated' );
4328
					}
4329
					self::state( 'module', $modules );
4330
					wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4331
					exit;
4332
				case 'unlink':
4333
					$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4334
					check_admin_referer( 'jetpack-unlink' );
4335
					self::log( 'unlink' );
4336
					Connection_Manager::disconnect_user();
4337
					self::state( 'message', 'unlinked' );
4338
					if ( 'sub-unlink' == $redirect ) {
4339
						wp_safe_redirect( admin_url() );
4340
					} else {
4341
						wp_safe_redirect( self::admin_url( array( 'page' => $redirect ) ) );
4342
					}
4343
					exit;
4344
				case 'onboard':
4345
					if ( ! current_user_can( 'manage_options' ) ) {
4346
						wp_safe_redirect( self::admin_url( 'page=jetpack' ) );
4347
					} else {
4348
						self::create_onboarding_token();
4349
						$url = $this->build_connect_url( true );
4350
4351
						if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4352
							$url = add_query_arg( 'onboarding', $token, $url );
4353
						}
4354
4355
						$calypso_env = $this->get_calypso_env();
4356
						if ( ! empty( $calypso_env ) ) {
4357
							$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4358
						}
4359
4360
						wp_redirect( $url );
4361
						exit;
4362
					}
4363
					exit;
4364
				default:
4365
					/**
4366
					 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4367
					 *
4368
					 * @since 2.6.0
4369
					 *
4370
					 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4371
					 */
4372
					do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4373
			}
4374
		}
4375
4376
		if ( ! $error = $error ? $error : self::state( 'error' ) ) {
4377
			self::activate_new_modules( true );
4378
		}
4379
4380
		$message_code = self::state( 'message' );
4381
		if ( self::state( 'optin-manage' ) ) {
4382
			$activated_manage = $message_code;
4383
			$message_code     = 'jetpack-manage';
4384
		}
4385
4386
		switch ( $message_code ) {
4387
			case 'jetpack-manage':
4388
				$sites_url = esc_url( Redirect::get_url( 'calypso-sites' ) );
4389
				// translators: %s is the URL to the "Sites" panel on wordpress.com.
4390
				$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>';
4391
				if ( $activated_manage ) {
0 ignored issues
show
Bug introduced by
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...
4392
					$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack' ) . '</strong>';
4393
				}
4394
				break;
4395
4396
		}
4397
4398
		$deactivated_plugins = self::state( 'deactivated_plugins' );
4399
4400
		if ( ! empty( $deactivated_plugins ) ) {
4401
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4402
			$deactivated_titles  = array();
4403
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4404
				if ( ! isset( $this->plugins_to_deactivate[ $deactivated_plugin ] ) ) {
4405
					continue;
4406
				}
4407
4408
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[ $deactivated_plugin ][1] ) . '</strong>';
4409
			}
4410
4411
			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...
4412
				if ( $this->message ) {
4413
					$this->message .= "<br /><br />\n";
4414
				}
4415
4416
				$this->message .= wp_sprintf(
4417
					_n(
4418
						'Jetpack contains the most recent version of the old %l plugin.',
4419
						'Jetpack contains the most recent versions of the old %l plugins.',
4420
						count( $deactivated_titles ),
4421
						'jetpack'
4422
					),
4423
					$deactivated_titles
4424
				);
4425
4426
				$this->message .= "<br />\n";
4427
4428
				$this->message .= _n(
4429
					'The old version has been deactivated and can be removed from your site.',
4430
					'The old versions have been deactivated and can be removed from your site.',
4431
					count( $deactivated_titles ),
4432
					'jetpack'
4433
				);
4434
			}
4435
		}
4436
4437
		$this->privacy_checks = self::state( 'privacy_checks' );
4438
4439
		if ( $this->message || $this->error || $this->privacy_checks ) {
4440
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4441
		}
4442
4443
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4444
	}
4445
4446
	function admin_notices() {
4447
4448
		if ( $this->error ) {
4449
			?>
4450
<div id="message" class="jetpack-message jetpack-err">
4451
	<div class="squeezer">
4452
		<h2>
4453
			<?php
4454
			echo wp_kses(
4455
				$this->error,
4456
				array(
4457
					'a'      => array( 'href' => array() ),
4458
					'small'  => true,
4459
					'code'   => true,
4460
					'strong' => true,
4461
					'br'     => true,
4462
					'b'      => true,
4463
				)
4464
			);
4465
			?>
4466
			</h2>
4467
			<?php	if ( $desc = self::state( 'error_description' ) ) : ?>
4468
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4469
<?php	endif; ?>
4470
	</div>
4471
</div>
4472
			<?php
4473
		}
4474
4475
		if ( $this->message ) {
4476
			?>
4477
<div id="message" class="jetpack-message">
4478
	<div class="squeezer">
4479
		<h2>
4480
			<?php
4481
			echo wp_kses(
4482
				$this->message,
4483
				array(
4484
					'strong' => array(),
4485
					'a'      => array( 'href' => true ),
4486
					'br'     => true,
4487
				)
4488
			);
4489
			?>
4490
			</h2>
4491
	</div>
4492
</div>
4493
			<?php
4494
		}
4495
4496
		if ( $this->privacy_checks ) :
4497
			$module_names = $module_slugs = array();
4498
4499
			$privacy_checks = explode( ',', $this->privacy_checks );
4500
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4501
			foreach ( $privacy_checks as $module_slug ) {
4502
				$module = self::get_module( $module_slug );
4503
				if ( ! $module ) {
4504
					continue;
4505
				}
4506
4507
				$module_slugs[] = $module_slug;
4508
				$module_names[] = "<strong>{$module['name']}</strong>";
4509
			}
4510
4511
			$module_slugs = join( ',', $module_slugs );
4512
			?>
4513
<div id="message" class="jetpack-message jetpack-err">
4514
	<div class="squeezer">
4515
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4516
		<p>
4517
			<?php
4518
			echo wp_kses(
4519
				wptexturize(
4520
					wp_sprintf(
4521
						_nx(
4522
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4523
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4524
							count( $privacy_checks ),
4525
							'%l = list of Jetpack module/feature names',
4526
							'jetpack'
4527
						),
4528
						$module_names
4529
					)
4530
				),
4531
				array( 'strong' => true )
4532
			);
4533
4534
			echo "\n<br />\n";
4535
4536
			echo wp_kses(
4537
				sprintf(
4538
					_nx(
4539
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4540
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4541
						count( $privacy_checks ),
4542
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4543
						'jetpack'
4544
					),
4545
					wp_nonce_url(
4546
						self::admin_url(
4547
							array(
4548
								'page'   => 'jetpack',
4549
								'action' => 'deactivate',
4550
								'module' => urlencode( $module_slugs ),
4551
							)
4552
						),
4553
						"jetpack_deactivate-$module_slugs"
4554
					),
4555
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4556
				),
4557
				array(
4558
					'a' => array(
4559
						'href'  => true,
4560
						'title' => true,
4561
					),
4562
				)
4563
			);
4564
			?>
4565
		</p>
4566
	</div>
4567
</div>
4568
			<?php
4569
endif;
4570
	}
4571
4572
	/**
4573
	 * We can't always respond to a signed XML-RPC request with a
4574
	 * helpful error message. In some circumstances, doing so could
4575
	 * leak information.
4576
	 *
4577
	 * Instead, track that the error occurred via a Jetpack_Option,
4578
	 * and send that data back in the heartbeat.
4579
	 * All this does is increment a number, but it's enough to find
4580
	 * trends.
4581
	 *
4582
	 * @param WP_Error $xmlrpc_error The error produced during
4583
	 *                               signature validation.
4584
	 */
4585
	function track_xmlrpc_error( $xmlrpc_error ) {
4586
		$code = is_wp_error( $xmlrpc_error )
4587
			? $xmlrpc_error->get_error_code()
0 ignored issues
show
Bug introduced by
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...
4588
			: 'should-not-happen';
4589
4590
		$xmlrpc_errors = Jetpack_Options::get_option( 'xmlrpc_errors', array() );
4591
		if ( isset( $xmlrpc_errors[ $code ] ) && $xmlrpc_errors[ $code ] ) {
4592
			// No need to update the option if we already have
4593
			// this code stored.
4594
			return;
4595
		}
4596
		$xmlrpc_errors[ $code ] = true;
4597
4598
		Jetpack_Options::update_option( 'xmlrpc_errors', $xmlrpc_errors, false );
0 ignored issues
show
Documentation introduced by
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...
4599
	}
4600
4601
	/**
4602
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4603
	 */
4604
	function stat( $group, $detail ) {
4605
		if ( ! isset( $this->stats[ $group ] ) ) {
4606
			$this->stats[ $group ] = array();
4607
		}
4608
		$this->stats[ $group ][] = $detail;
4609
	}
4610
4611
	/**
4612
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4613
	 */
4614
	function do_stats( $method = '' ) {
4615
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4616
			foreach ( $this->stats as $group => $stats ) {
4617
				if ( is_array( $stats ) && count( $stats ) ) {
4618
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4619
					if ( 'server_side' === $method ) {
4620
						self::do_server_side_stat( $args );
4621
					} else {
4622
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4623
					}
4624
				}
4625
				unset( $this->stats[ $group ] );
4626
			}
4627
		}
4628
	}
4629
4630
	/**
4631
	 * Runs stats code for a one-off, server-side.
4632
	 *
4633
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4634
	 *
4635
	 * @return bool If it worked.
4636
	 */
4637
	static function do_server_side_stat( $args ) {
4638
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4639
		if ( is_wp_error( $response ) ) {
4640
			return false;
4641
		}
4642
4643
		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4644
			return false;
4645
		}
4646
4647
		return true;
4648
	}
4649
4650
	/**
4651
	 * Builds the stats url.
4652
	 *
4653
	 * @param $args array|string The arguments to append to the URL.
4654
	 *
4655
	 * @return string The URL to be pinged.
4656
	 */
4657
	static function build_stats_url( $args ) {
4658
		$defaults = array(
4659
			'v'    => 'wpcom2',
4660
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4661
		);
4662
		$args     = wp_parse_args( $args, $defaults );
0 ignored issues
show
Documentation introduced by
$defaults is of type array<string,string,{"v"...ring","rand":"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...
4663
		/**
4664
		 * Filter the URL used as the Stats tracking pixel.
4665
		 *
4666
		 * @since 2.3.2
4667
		 *
4668
		 * @param string $url Base URL used as the Stats tracking pixel.
4669
		 */
4670
		$base_url = apply_filters(
4671
			'jetpack_stats_base_url',
4672
			'https://pixel.wp.com/g.gif'
4673
		);
4674
		$url      = add_query_arg( $args, $base_url );
4675
		return $url;
4676
	}
4677
4678
	/**
4679
	 * Get the role of the current user.
4680
	 *
4681
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_current_user_to_role() instead.
4682
	 *
4683
	 * @access public
4684
	 * @static
4685
	 *
4686
	 * @return string|boolean Current user's role, false if not enough capabilities for any of the roles.
4687
	 */
4688
	public static function translate_current_user_to_role() {
4689
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4690
4691
		$roles = new Roles();
4692
		return $roles->translate_current_user_to_role();
4693
	}
4694
4695
	/**
4696
	 * Get the role of a particular user.
4697
	 *
4698
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_user_to_role() instead.
4699
	 *
4700
	 * @access public
4701
	 * @static
4702
	 *
4703
	 * @param \WP_User $user User object.
4704
	 * @return string|boolean User's role, false if not enough capabilities for any of the roles.
4705
	 */
4706
	public static function translate_user_to_role( $user ) {
4707
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4708
4709
		$roles = new Roles();
4710
		return $roles->translate_user_to_role( $user );
4711
	}
4712
4713
	/**
4714
	 * Get the minimum capability for a role.
4715
	 *
4716
	 * @deprecated 7.6 Use Automattic\Jetpack\Roles::translate_role_to_cap() instead.
4717
	 *
4718
	 * @access public
4719
	 * @static
4720
	 *
4721
	 * @param string $role Role name.
4722
	 * @return string|boolean Capability, false if role isn't mapped to any capabilities.
4723
	 */
4724
	public static function translate_role_to_cap( $role ) {
4725
		_deprecated_function( __METHOD__, 'jetpack-7.6.0' );
4726
4727
		$roles = new Roles();
4728
		return $roles->translate_role_to_cap( $role );
4729
	}
4730
4731
	/**
4732
	 * Sign a user role with the master access token.
4733
	 * If not specified, will default to the current user.
4734
	 *
4735
	 * @deprecated since 7.7
4736
	 * @see Automattic\Jetpack\Connection\Manager::sign_role()
4737
	 *
4738
	 * @access public
4739
	 * @static
4740
	 *
4741
	 * @param string $role    User role.
4742
	 * @param int    $user_id ID of the user.
0 ignored issues
show
Documentation introduced by
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...
4743
	 * @return string Signed user role.
4744
	 */
4745
	public static function sign_role( $role, $user_id = null ) {
4746
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::sign_role' );
4747
		return self::connection()->sign_role( $role, $user_id );
4748
	}
4749
4750
	/**
4751
	 * Builds a URL to the Jetpack connection auth page
4752
	 *
4753
	 * @since 3.9.5
4754
	 *
4755
	 * @param bool        $raw If true, URL will not be escaped.
4756
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4757
	 *                              If string, will be a custom redirect.
4758
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4759
	 * @param bool        $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4760
	 *
4761
	 * @return string Connect URL
4762
	 */
4763
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4764
		$site_id    = Jetpack_Options::get_option( 'id' );
4765
		$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...
4766
4767
		if ( $register || ! $blog_token || ! $site_id ) {
4768
			$url = self::nonce_url_no_esc( self::admin_url( 'action=register' ), 'jetpack-register' );
4769
4770
			if ( ! empty( $redirect ) ) {
4771
				$url = add_query_arg(
4772
					'redirect',
4773
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4774
					$url
4775
				);
4776
			}
4777
4778
			if ( is_network_admin() ) {
4779
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4780
			}
4781
4782
			$calypso_env = self::get_calypso_env();
4783
4784
			if ( ! empty( $calypso_env ) ) {
4785
				$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4786
			}
4787
		} else {
4788
4789
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4790
			// because otherwise this logic can get us in to a loop.
4791
			$last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) );
4792
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4793
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4794
4795
				$response = Client::wpcom_json_api_request_as_blog(
4796
					sprintf( '/sites/%d', $site_id ) . '?force=wpcom',
4797
					'1.1'
4798
				);
4799
4800
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4801
4802
					// Generating a register URL instead to refresh the existing token
4803
					return $this->build_connect_url( $raw, $redirect, $from, true );
4804
				}
4805
			}
4806
4807
			$url = $this->build_authorize_url( $redirect );
0 ignored issues
show
Bug introduced by
It seems like $redirect defined by parameter $redirect on line 4763 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...
4808
		}
4809
4810
		if ( $from ) {
4811
			$url = add_query_arg( 'from', $from, $url );
4812
		}
4813
4814
		$url = $raw ? esc_url_raw( $url ) : esc_url( $url );
4815
		/**
4816
		 * Filter the URL used when connecting a user to a WordPress.com account.
4817
		 *
4818
		 * @since 8.1.0
4819
		 *
4820
		 * @param string $url Connection URL.
4821
		 * @param bool   $raw If true, URL will not be escaped.
4822
		 */
4823
		return apply_filters( 'jetpack_build_connection_url', $url, $raw );
0 ignored issues
show
Unused Code introduced by
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...
4824
	}
4825
4826
	public static function build_authorize_url( $redirect = false, $iframe = false ) {
4827
4828
		add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4829
		add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4830
		add_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4831
4832
		if ( $iframe ) {
4833
			add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4834
		}
4835
4836
		$c8n = self::connection();
4837
		$url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );
0 ignored issues
show
Documentation introduced by
$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...
4838
4839
		remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
4840
		remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
4841
		remove_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );
4842
4843
		if ( $iframe ) {
4844
			remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
4845
		}
4846
4847
		return $url;
4848
	}
4849
4850
	/**
4851
	 * Filters the connection URL parameter array.
4852
	 *
4853
	 * @param array $args default URL parameters used by the package.
4854
	 * @return array the modified URL arguments array.
4855
	 */
4856
	public static function filter_connect_request_body( $args ) {
4857
		if (
4858
			Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4859
			&& include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
4860
		) {
4861
			$gp_locale      = GP_Locales::by_field( 'wp_locale', get_locale() );
4862
			$args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
4863
				? $gp_locale->slug
4864
				: '';
4865
		}
4866
4867
		$tracking        = new Tracking();
4868
		$tracks_identity = $tracking->tracks_get_identity( $args['state'] );
4869
4870
		$args = array_merge(
4871
			$args,
4872
			array(
4873
				'_ui' => $tracks_identity['_ui'],
4874
				'_ut' => $tracks_identity['_ut'],
4875
			)
4876
		);
4877
4878
		$calypso_env = self::get_calypso_env();
4879
4880
		if ( ! empty( $calypso_env ) ) {
4881
			$args['calypso_env'] = $calypso_env;
4882
		}
4883
4884
		return $args;
4885
	}
4886
4887
	/**
4888
	 * Filters the URL that will process the connection data. It can be different from the URL
4889
	 * that we send the user to after everything is done.
4890
	 *
4891
	 * @param String $processing_url the default redirect URL used by the package.
4892
	 * @return String the modified URL.
4893
	 */
4894
	public static function filter_connect_processing_url( $processing_url ) {
4895
		$processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
4896
		return $processing_url;
4897
	}
4898
4899
	/**
4900
	 * Filters the redirection URL that is used for connect requests. The redirect
4901
	 * URL should return the user back to the Jetpack console.
4902
	 *
4903
	 * @param String $redirect the default redirect URL used by the package.
4904
	 * @return String the modified URL.
4905
	 */
4906
	public static function filter_connect_redirect_url( $redirect ) {
4907
		$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4908
		$redirect           = $redirect
4909
			? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4910
			: $jetpack_admin_page;
4911
4912
		if ( isset( $_REQUEST['is_multisite'] ) ) {
4913
			$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4914
		}
4915
4916
		return $redirect;
4917
	}
4918
4919
	/**
4920
	 * This action fires at the beginning of the Manager::authorize method.
4921
	 */
4922
	public static function authorize_starting() {
4923
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
4924
		// Checking if site has been active/connected previously before recording unique connection.
4925
		if ( ! $jetpack_unique_connection ) {
4926
			// jetpack_unique_connection option has never been set.
4927
			$jetpack_unique_connection = array(
4928
				'connected'    => 0,
4929
				'disconnected' => 0,
4930
				'version'      => '3.6.1',
4931
			);
4932
4933
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
4934
4935
			// Track unique connection.
4936
			$jetpack = self::init();
4937
4938
			$jetpack->stat( 'connections', 'unique-connection' );
4939
			$jetpack->do_stats( 'server_side' );
4940
		}
4941
4942
		// Increment number of times connected.
4943
		$jetpack_unique_connection['connected'] += 1;
4944
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
4945
	}
4946
4947
	/**
4948
	 * This action fires at the end of the Manager::authorize method when a secondary user is
4949
	 * linked.
4950
	 */
4951
	public static function authorize_ending_linked() {
4952
		// Don't activate anything since we are just connecting a user.
4953
		self::state( 'message', 'linked' );
4954
	}
4955
4956
	/**
4957
	 * This action fires at the end of the Manager::authorize method when the master user is
4958
	 * authorized.
4959
	 *
4960
	 * @param array $data The request data.
4961
	 */
4962
	public static function authorize_ending_authorized( $data ) {
4963
		// If this site has been through the Jetpack Onboarding flow, delete the onboarding token.
4964
		self::invalidate_onboarding_token();
4965
4966
		// If redirect_uri is SSO, ensure SSO module is enabled.
4967
		parse_str( wp_parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
0 ignored issues
show
Unused Code introduced by
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...
4968
4969
		/** This filter is documented in class.jetpack-cli.php */
4970
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
4971
4972
		$activate_sso = (
4973
			isset( $redirect_options['action'] ) &&
4974
			'jetpack-sso' === $redirect_options['action'] &&
4975
			$jetpack_start_enable_sso
4976
		);
4977
4978
		$do_redirect_on_error = ( 'client' === $data['auth_type'] );
4979
4980
		self::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
4981
	}
4982
4983
	/**
4984
	 * Get our assumed site creation date.
4985
	 * Calculated based on the earlier date of either:
4986
	 * - Earliest admin user registration date.
4987
	 * - Earliest date of post of any post type.
4988
	 *
4989
	 * @since 7.2.0
4990
	 * @deprecated since 7.8.0
4991
	 *
4992
	 * @return string Assumed site creation date and time.
4993
	 */
4994
	public static function get_assumed_site_creation_date() {
4995
		_deprecated_function( __METHOD__, 'jetpack-7.8', 'Automattic\\Jetpack\\Connection\\Manager' );
4996
		return self::connection()->get_assumed_site_creation_date();
4997
	}
4998
4999 View Code Duplication
	public static function apply_activation_source_to_args( &$args ) {
5000
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
5001
5002
		if ( $activation_source_name ) {
5003
			$args['_as'] = urlencode( $activation_source_name );
5004
		}
5005
5006
		if ( $activation_source_keyword ) {
5007
			$args['_ak'] = urlencode( $activation_source_keyword );
5008
		}
5009
	}
5010
5011
	function build_reconnect_url( $raw = false ) {
5012
		$url = wp_nonce_url( self::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
5013
		return $raw ? $url : esc_url( $url );
5014
	}
5015
5016
	public static function admin_url( $args = null ) {
5017
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
0 ignored issues
show
Documentation introduced by
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...
5018
		$url  = add_query_arg( $args, admin_url( 'admin.php' ) );
5019
		return $url;
5020
	}
5021
5022
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
5023
		$actionurl = str_replace( '&amp;', '&', $actionurl );
5024
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
5025
	}
5026
5027
	function dismiss_jetpack_notice() {
5028
5029
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
5030
			return;
5031
		}
5032
5033
		switch ( $_GET['jetpack-notice'] ) {
5034
			case 'dismiss':
5035
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
5036
5037
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
5038
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
5039
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
5040
				}
5041
				break;
5042
		}
5043
	}
5044
5045
	public static function sort_modules( $a, $b ) {
5046
		if ( $a['sort'] == $b['sort'] ) {
5047
			return 0;
5048
		}
5049
5050
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5051
	}
5052
5053
	function ajax_recheck_ssl() {
5054
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
5055
		$result = self::permit_ssl( true );
5056
		wp_send_json(
5057
			array(
5058
				'enabled' => $result,
5059
				'message' => get_transient( 'jetpack_https_test_message' ),
5060
			)
5061
		);
5062
	}
5063
5064
	/* Client API */
5065
5066
	/**
5067
	 * Returns the requested Jetpack API URL
5068
	 *
5069
	 * @deprecated since 7.7
5070
	 * @return string
5071
	 */
5072
	public static function api_url( $relative_url ) {
5073
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::api_url' );
5074
		$connection = self::connection();
5075
		return $connection->api_url( $relative_url );
5076
	}
5077
5078
	/**
5079
	 * @deprecated 8.0 Use Automattic\Jetpack\Connection\Utils::fix_url_for_bad_hosts() instead.
5080
	 *
5081
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
5082
	 */
5083
	public static function fix_url_for_bad_hosts( $url ) {
5084
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Utils::fix_url_for_bad_hosts' );
5085
		return Connection_Utils::fix_url_for_bad_hosts( $url );
5086
	}
5087
5088
	public static function verify_onboarding_token( $token_data, $token, $request_data ) {
5089
		// Default to a blog token.
5090
		$token_type = 'blog';
5091
5092
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5093
		if ( isset( $request_data ) || ! empty( $_GET['onboarding'] ) ) {
5094
			if ( ! empty( $_GET['onboarding'] ) ) {
5095
				$jpo = $_GET;
5096
			} else {
5097
				$jpo = json_decode( $request_data, true );
5098
			}
5099
5100
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5101
			$jpo_user  = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5102
5103
			if (
5104
				isset( $jpo_user )
5105
				&& isset( $jpo_token )
5106
				&& is_email( $jpo_user )
5107
				&& ctype_alnum( $jpo_token )
5108
				&& isset( $_GET['rest_route'] )
5109
				&& self::validate_onboarding_token_action(
5110
					$jpo_token,
5111
					$_GET['rest_route']
5112
				)
5113
			) {
5114
				$jp_user = get_user_by( 'email', $jpo_user );
5115
				if ( is_a( $jp_user, 'WP_User' ) ) {
5116
					wp_set_current_user( $jp_user->ID );
5117
					$user_can = is_multisite()
5118
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5119
						: current_user_can( 'manage_options' );
5120
					if ( $user_can ) {
5121
						$token_type              = 'user';
5122
						$token->external_user_id = $jp_user->ID;
5123
					}
5124
				}
5125
			}
5126
5127
			$token_data['type']    = $token_type;
5128
			$token_data['user_id'] = $token->external_user_id;
5129
		}
5130
5131
		return $token_data;
5132
	}
5133
5134
	/**
5135
	 * Create a random secret for validating onboarding payload
5136
	 *
5137
	 * @return string Secret token
5138
	 */
5139
	public static function create_onboarding_token() {
5140
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
5141
			$token = wp_generate_password( 32, false );
5142
			Jetpack_Options::update_option( 'onboarding', $token );
5143
		}
5144
5145
		return $token;
5146
	}
5147
5148
	/**
5149
	 * Remove the onboarding token
5150
	 *
5151
	 * @return bool True on success, false on failure
5152
	 */
5153
	public static function invalidate_onboarding_token() {
5154
		return Jetpack_Options::delete_option( 'onboarding' );
5155
	}
5156
5157
	/**
5158
	 * Validate an onboarding token for a specific action
5159
	 *
5160
	 * @return boolean True if token/action pair is accepted, false if not
5161
	 */
5162
	public static function validate_onboarding_token_action( $token, $action ) {
5163
		// Compare tokens, bail if tokens do not match
5164
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5165
			return false;
5166
		}
5167
5168
		// List of valid actions we can take
5169
		$valid_actions = array(
5170
			'/jetpack/v4/settings',
5171
		);
5172
5173
		// Only allow valid actions.
5174
		if ( ! in_array( $action, $valid_actions ) ) {
5175
			return false;
5176
		}
5177
5178
		return true;
5179
	}
5180
5181
	/**
5182
	 * Checks to see if the URL is using SSL to connect with Jetpack
5183
	 *
5184
	 * @since 2.3.3
5185
	 * @return boolean
5186
	 */
5187
	public static function permit_ssl( $force_recheck = false ) {
5188
		// Do some fancy tests to see if ssl is being supported
5189
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5190
			$message = '';
5191
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5192
				$ssl = 0;
5193
			} else {
5194
				switch ( JETPACK_CLIENT__HTTPS ) {
5195
					case 'NEVER':
5196
						$ssl     = 0;
5197
						$message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' );
5198
						break;
5199
					case 'ALWAYS':
5200
					case 'AUTO':
5201
					default:
5202
						$ssl = 1;
5203
						break;
5204
				}
5205
5206
				// If it's not 'NEVER', test to see
5207
				if ( $ssl ) {
5208
					if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5209
						$ssl     = 0;
5210
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
5211
					} else {
5212
						$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5213
						if ( is_wp_error( $response ) ) {
5214
							$ssl     = 0;
5215
							$message = __( 'WordPress reports no SSL support', 'jetpack' );
5216
						} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5217
							$ssl     = 0;
5218
							$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5219
						}
5220
					}
5221
				}
5222
			}
5223
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5224
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5225
		}
5226
5227
		return (bool) $ssl;
5228
	}
5229
5230
	/*
5231
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working.
5232
	 */
5233
	public function alert_auto_ssl_fail() {
5234
		if ( ! current_user_can( 'manage_options' ) ) {
5235
			return;
5236
		}
5237
5238
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5239
		?>
5240
5241
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5242
			<div class="jp-banner__content">
5243
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5244
				<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>
5245
				<p>
5246
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5247
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5248
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5249
				</p>
5250
				<p>
5251
					<?php
5252
					printf(
5253
						__( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
5254
						esc_url( self::admin_url( array( 'page' => 'jetpack-debugger' ) ) ),
5255
						esc_url( Redirect::get_url( 'jetpack-support-getting-started-troubleshooting-tips' ) )
5256
					);
5257
					?>
5258
				</p>
5259
			</div>
5260
		</div>
5261
		<style>
5262
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5263
		</style>
5264
		<script type="text/javascript">
5265
			jQuery( document ).ready( function( $ ) {
5266
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5267
					var $this = $( this );
5268
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5269
					$( '#jetpack-recheck-ssl-output' ).html( '' );
5270
					e.preventDefault();
5271
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5272
					$.post( ajaxurl, data )
5273
					  .done( function( response ) {
5274
						  if ( response.enabled ) {
5275
							  $( '#jetpack-ssl-warning' ).hide();
5276
						  } else {
5277
							  this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5278
							  $( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5279
						  }
5280
					  }.bind( $this ) );
5281
				} );
5282
			} );
5283
		</script>
5284
5285
		<?php
5286
	}
5287
5288
	/**
5289
	 * Returns the Jetpack XML-RPC API
5290
	 *
5291
	 * @deprecated 8.0 Use Connection_Manager instead.
5292
	 * @return string
5293
	 */
5294
	public static function xmlrpc_api_url() {
5295
		_deprecated_function( __METHOD__, 'jetpack-8.0', 'Automattic\\Jetpack\\Connection\\Manager::xmlrpc_api_url()' );
5296
		return self::connection()->xmlrpc_api_url();
5297
	}
5298
5299
	/**
5300
	 * Returns the connection manager object.
5301
	 *
5302
	 * @return Automattic\Jetpack\Connection\Manager
5303
	 */
5304
	public static function connection() {
5305
		$jetpack = static::init();
5306
5307
		// If the connection manager hasn't been instantiated, do that now.
5308
		if ( ! $jetpack->connection_manager ) {
5309
			$jetpack->connection_manager = new Connection_Manager( 'jetpack' );
5310
		}
5311
5312
		return $jetpack->connection_manager;
5313
	}
5314
5315
	/**
5316
	 * Creates two secret tokens and the end of life timestamp for them.
5317
	 *
5318
	 * Note these tokens are unique per call, NOT static per site for connecting.
5319
	 *
5320
	 * @since 2.6
5321
	 * @param String  $action  The action name.
5322
	 * @param Integer $user_id The user identifier.
0 ignored issues
show
Documentation introduced by
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...
5323
	 * @param Integer $exp     Expiration time in seconds.
5324
	 * @return array
5325
	 */
5326
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5327
		return self::connection()->generate_secrets( $action, $user_id, $exp );
5328
	}
5329
5330
	public static function get_secrets( $action, $user_id ) {
5331
		$secrets = self::connection()->get_secrets( $action, $user_id );
5332
5333
		if ( Connection_Manager::SECRETS_MISSING === $secrets ) {
5334
			return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
0 ignored issues
show
Unused Code introduced by
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...
5335
		}
5336
5337
		if ( Connection_Manager::SECRETS_EXPIRED === $secrets ) {
5338
			return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
0 ignored issues
show
Unused Code introduced by
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...
5339
		}
5340
5341
		return $secrets;
5342
	}
5343
5344
	/**
5345
	 * @deprecated 7.5 Use Connection_Manager instead.
5346
	 *
5347
	 * @param $action
5348
	 * @param $user_id
5349
	 */
5350
	public static function delete_secrets( $action, $user_id ) {
5351
		return self::connection()->delete_secrets( $action, $user_id );
5352
	}
5353
5354
	/**
5355
	 * Builds the timeout limit for queries talking with the wpcom servers.
5356
	 *
5357
	 * Based on local php max_execution_time in php.ini
5358
	 *
5359
	 * @since 2.6
5360
	 * @return int
5361
	 * @deprecated
5362
	 **/
5363
	public function get_remote_query_timeout_limit() {
5364
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
5365
		return self::get_max_execution_time();
5366
	}
5367
5368
	/**
5369
	 * Builds the timeout limit for queries talking with the wpcom servers.
5370
	 *
5371
	 * Based on local php max_execution_time in php.ini
5372
	 *
5373
	 * @since 5.4
5374
	 * @return int
5375
	 **/
5376
	public static function get_max_execution_time() {
5377
		$timeout = (int) ini_get( 'max_execution_time' );
5378
5379
		// Ensure exec time set in php.ini
5380
		if ( ! $timeout ) {
5381
			$timeout = 30;
5382
		}
5383
		return $timeout;
5384
	}
5385
5386
	/**
5387
	 * Sets a minimum request timeout, and returns the current timeout
5388
	 *
5389
	 * @since 5.4
5390
	 **/
5391 View Code Duplication
	public static function set_min_time_limit( $min_timeout ) {
5392
		$timeout = self::get_max_execution_time();
5393
		if ( $timeout < $min_timeout ) {
5394
			$timeout = $min_timeout;
5395
			set_time_limit( $timeout );
5396
		}
5397
		return $timeout;
5398
	}
5399
5400
	/**
5401
	 * Takes the response from the Jetpack register new site endpoint and
5402
	 * verifies it worked properly.
5403
	 *
5404
	 * @since 2.6
5405
	 * @deprecated since 7.7.0
5406
	 * @see Automattic\Jetpack\Connection\Manager::validate_remote_register_response()
5407
	 **/
5408
	public function validate_remote_register_response() {
5409
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::validate_remote_register_response' );
5410
	}
5411
5412
	/**
5413
	 * @return bool|WP_Error
5414
	 */
5415
	public static function register() {
5416
		$tracking = new Tracking();
5417
		$tracking->record_user_event( 'jpc_register_begin' );
5418
5419
		add_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5420
5421
		$connection   = self::connection();
5422
		$registration = $connection->register();
5423
5424
		remove_filter( 'jetpack_register_request_body', array( __CLASS__, 'filter_register_request_body' ) );
5425
5426
		if ( ! $registration || is_wp_error( $registration ) ) {
5427
			return $registration;
5428
		}
5429
5430
		return true;
5431
	}
5432
5433
	/**
5434
	 * Filters the registration request body to include tracking properties.
5435
	 *
5436
	 * @param array $properties
5437
	 * @return array amended properties.
5438
	 */
5439 View Code Duplication
	public static function filter_register_request_body( $properties ) {
5440
		$tracking        = new Tracking();
5441
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5442
5443
		return array_merge(
5444
			$properties,
5445
			array(
5446
				'_ui' => $tracks_identity['_ui'],
5447
				'_ut' => $tracks_identity['_ut'],
5448
			)
5449
		);
5450
	}
5451
5452
	/**
5453
	 * Filters the token request body to include tracking properties.
5454
	 *
5455
	 * @param array $properties
5456
	 * @return array amended properties.
5457
	 */
5458 View Code Duplication
	public static function filter_token_request_body( $properties ) {
5459
		$tracking        = new Tracking();
5460
		$tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
5461
5462
		return array_merge(
5463
			$properties,
5464
			array(
5465
				'_ui' => $tracks_identity['_ui'],
5466
				'_ut' => $tracks_identity['_ut'],
5467
			)
5468
		);
5469
	}
5470
5471
	/**
5472
	 * If the db version is showing something other that what we've got now, bump it to current.
5473
	 *
5474
	 * @return bool: True if the option was incorrect and updated, false if nothing happened.
0 ignored issues
show
Documentation introduced by
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...
5475
	 */
5476
	public static function maybe_set_version_option() {
5477
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5478
		if ( JETPACK__VERSION != $version ) {
5479
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5480
5481
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5482
				/** This action is documented in class.jetpack.php */
5483
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5484
			}
5485
5486
			return true;
5487
		}
5488
		return false;
5489
	}
5490
5491
	/* Client Server API */
5492
5493
	/**
5494
	 * Loads the Jetpack XML-RPC client.
5495
	 * No longer necessary, as the XML-RPC client will be automagically loaded.
5496
	 *
5497
	 * @deprecated since 7.7.0
5498
	 */
5499
	public static function load_xml_rpc_client() {
5500
		_deprecated_function( __METHOD__, 'jetpack-7.7' );
5501
	}
5502
5503
	/**
5504
	 * Resets the saved authentication state in between testing requests.
5505
	 */
5506
	public function reset_saved_auth_state() {
5507
		$this->rest_authentication_status = null;
5508
5509
		if ( ! $this->connection_manager ) {
5510
			$this->connection_manager = new Connection_Manager();
5511
		}
5512
5513
		$this->connection_manager->reset_saved_auth_state();
5514
	}
5515
5516
	/**
5517
	 * Verifies the signature of the current request.
5518
	 *
5519
	 * @deprecated since 7.7.0
5520
	 * @see Automattic\Jetpack\Connection\Manager::verify_xml_rpc_signature()
5521
	 *
5522
	 * @return false|array
5523
	 */
5524
	public function verify_xml_rpc_signature() {
5525
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::verify_xml_rpc_signature' );
5526
		return self::connection()->verify_xml_rpc_signature();
5527
	}
5528
5529
	/**
5530
	 * Verifies the signature of the current request.
5531
	 *
5532
	 * This function has side effects and should not be used. Instead,
5533
	 * use the memoized version `->verify_xml_rpc_signature()`.
5534
	 *
5535
	 * @deprecated since 7.7.0
5536
	 * @see Automattic\Jetpack\Connection\Manager::internal_verify_xml_rpc_signature()
5537
	 * @internal
5538
	 */
5539
	private function internal_verify_xml_rpc_signature() {
5540
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::internal_verify_xml_rpc_signature' );
5541
	}
5542
5543
	/**
5544
	 * Authenticates XML-RPC and other requests from the Jetpack Server.
5545
	 *
5546
	 * @deprecated since 7.7.0
5547
	 * @see Automattic\Jetpack\Connection\Manager::authenticate_jetpack()
5548
	 *
5549
	 * @param \WP_User|mixed $user     User object if authenticated.
5550
	 * @param string         $username Username.
5551
	 * @param string         $password Password string.
5552
	 * @return \WP_User|mixed Authenticated user or error.
5553
	 */
5554 View Code Duplication
	public function authenticate_jetpack( $user, $username, $password ) {
5555
		_deprecated_function( __METHOD__, 'jetpack-7.7', 'Automattic\\Jetpack\\Connection\\Manager::authenticate_jetpack' );
5556
5557
		if ( ! $this->connection_manager ) {
5558
			$this->connection_manager = new Connection_Manager();
5559
		}
5560
5561
		return $this->connection_manager->authenticate_jetpack( $user, $username, $password );
5562
	}
5563
5564
	// Authenticates requests from Jetpack server to WP REST API endpoints.
5565
	// Uses the existing XMLRPC request signing implementation.
5566
	function wp_rest_authenticate( $user ) {
5567
		if ( ! empty( $user ) ) {
5568
			// Another authentication method is in effect.
5569
			return $user;
5570
		}
5571
5572
		if ( ! isset( $_GET['_for'] ) || $_GET['_for'] !== 'jetpack' ) {
5573
			// Nothing to do for this authentication method.
5574
			return null;
5575
		}
5576
5577
		if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
5578
			// Nothing to do for this authentication method.
5579
			return null;
5580
		}
5581
5582
		// Ensure that we always have the request body available.  At this
5583
		// point, the WP REST API code to determine the request body has not
5584
		// run yet.  That code may try to read from 'php://input' later, but
5585
		// this can only be done once per request in PHP versions prior to 5.6.
5586
		// So we will go ahead and perform this read now if needed, and save
5587
		// the request body where both the Jetpack signature verification code
5588
		// and the WP REST API code can see it.
5589
		if ( ! isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ) {
5590
			$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents( 'php://input' );
5591
		}
5592
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5593
5594
		// Only support specific request parameters that have been tested and
5595
		// are known to work with signature verification.  A different method
5596
		// can be passed to the WP REST API via the '?_method=' parameter if
5597
		// needed.
5598
		if ( $_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
5599
			$this->rest_authentication_status = new WP_Error(
5600
				'rest_invalid_request',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_request'.

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...
5601
				__( 'This request method is not supported.', 'jetpack' ),
5602
				array( 'status' => 400 )
5603
			);
5604
			return null;
5605
		}
5606
		if ( $_SERVER['REQUEST_METHOD'] !== 'POST' && ! empty( $this->HTTP_RAW_POST_DATA ) ) {
5607
			$this->rest_authentication_status = new WP_Error(
5608
				'rest_invalid_request',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_request'.

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...
5609
				__( 'This request method does not support body parameters.', 'jetpack' ),
5610
				array( 'status' => 400 )
5611
			);
5612
			return null;
5613
		}
5614
5615
		if ( ! $this->connection_manager ) {
5616
			$this->connection_manager = new Connection_Manager();
5617
		}
5618
5619
		$verified = $this->connection_manager->verify_xml_rpc_signature();
5620
5621
		if (
5622
			$verified &&
5623
			isset( $verified['type'] ) &&
5624
			'user' === $verified['type'] &&
5625
			! empty( $verified['user_id'] )
5626
		) {
5627
			// Authentication successful.
5628
			$this->rest_authentication_status = true;
5629
			return $verified['user_id'];
5630
		}
5631
5632
		// Something else went wrong.  Probably a signature error.
5633
		$this->rest_authentication_status = new WP_Error(
5634
			'rest_invalid_signature',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_signature'.

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