Completed
Push — add/plugin-search-hints ( d3403f...c10220 )
by
unknown
22:54 queued 15:37
created

Jetpack::opt_in_jetpack_manage_url()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
Options:
5
jetpack_options (array)
6
	An array of options.
7
	@see Jetpack_Options::get_option_names()
8
9
jetpack_register (string)
10
	Temporary verification secrets.
11
12
jetpack_activated (int)
13
	1: the plugin was activated normally
14
	2: the plugin was activated on this site because of a network-wide activation
15
	3: the plugin was auto-installed
16
	4: the plugin was manually disconnected (but is still installed)
17
18
jetpack_active_modules (array)
19
	Array of active module slugs.
20
21
jetpack_do_activate (bool)
22
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
23
*/
24
25
require_once( JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php' );
26
27
class Jetpack {
28
	public $xmlrpc_server = null;
29
30
	private $xmlrpc_verification = null;
31
	private $rest_authentication_status = null;
32
33
	public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA']
34
35
	/**
36
	 * @var array The handles of styles that are concatenated into jetpack.css.
37
	 *
38
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
39
	 */
40
	public $concatenated_style_handles = array(
41
		'jetpack-carousel',
42
		'grunion.css',
43
		'the-neverending-homepage',
44
		'jetpack_likes',
45
		'jetpack_related-posts',
46
		'sharedaddy',
47
		'jetpack-slideshow',
48
		'presentations',
49
		'quiz',
50
		'jetpack-subscriptions',
51
		'jetpack-responsive-videos-style',
52
		'jetpack-social-menu',
53
		'tiled-gallery',
54
		'jetpack_display_posts_widget',
55
		'gravatar-profile-widget',
56
		'goodreads-widget',
57
		'jetpack_social_media_icons_widget',
58
		'jetpack-top-posts-widget',
59
		'jetpack_image_widget',
60
		'jetpack-my-community-widget',
61
		'jetpack-authors-widget',
62
		'wordads',
63
		'eu-cookie-law-style',
64
		'flickr-widget-style',
65
		'jetpack-search-widget',
66
		'jetpack-simple-payments-widget-style',
67
		'jetpack-widget-social-icons-styles',
68
	);
69
70
	/**
71
	 * Contains all assets that have had their URL rewritten to minified versions.
72
	 *
73
	 * @var array
74
	 */
75
	static $min_assets = array();
76
77
	public $plugins_to_deactivate = array(
78
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
79
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
80
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
81
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
82
		'after-the-deadline'  => array( 'after-the-deadline/after-the-deadline.php', 'After The Deadline' ),
83
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
84
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
85
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
86
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
87
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
88
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
89
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
90
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
91
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
92
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' )
93
	);
94
95
	static $capability_translations = array(
96
		'administrator' => 'manage_options',
97
		'editor'        => 'edit_others_posts',
98
		'author'        => 'publish_posts',
99
		'contributor'   => 'edit_posts',
100
		'subscriber'    => 'read',
101
	);
102
103
	/**
104
	 * Map of modules that have conflicts with plugins and should not be auto-activated
105
	 * if the plugins are active.  Used by filter_default_modules
106
	 *
107
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
108
	 * change `module-slug` and add this to your plugin:
109
	 *
110
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
111
	 * function my_jetpack_get_default_modules( $modules ) {
112
	 *     return array_diff( $modules, array( 'module-slug' ) );
113
	 * }
114
	 *
115
	 * @var array
116
	 */
117
	private $conflicting_plugins = array(
118
		'comments'          => array(
119
			'Intense Debate'                       => 'intensedebate/intensedebate.php',
120
			'Disqus'                               => 'disqus-comment-system/disqus.php',
121
			'Livefyre'                             => 'livefyre-comments/livefyre.php',
122
			'Comments Evolved for WordPress'       => 'gplus-comments/comments-evolved.php',
123
			'Google+ Comments'                     => 'google-plus-comments/google-plus-comments.php',
124
			'WP-SpamShield Anti-Spam'              => 'wp-spamshield/wp-spamshield.php',
125
		),
126
		'comment-likes' => array(
127
			'Epoch'                                => 'epoch/plugincore.php',
128
		),
129
		'contact-form'      => array(
130
			'Contact Form 7'                       => 'contact-form-7/wp-contact-form-7.php',
131
			'Gravity Forms'                        => 'gravityforms/gravityforms.php',
132
			'Contact Form Plugin'                  => 'contact-form-plugin/contact_form.php',
133
			'Easy Contact Forms'                   => 'easy-contact-forms/easy-contact-forms.php',
134
			'Fast Secure Contact Form'             => 'si-contact-form/si-contact-form.php',
135
			'Ninja Forms'                          => 'ninja-forms/ninja-forms.php',
136
		),
137
		'minileven'         => array(
138
			'WPtouch'                              => 'wptouch/wptouch.php',
139
		),
140
		'latex'             => array(
141
			'LaTeX for WordPress'                  => 'latex/latex.php',
142
			'Youngwhans Simple Latex'              => 'youngwhans-simple-latex/yw-latex.php',
143
			'Easy WP LaTeX'                        => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
144
			'MathJax-LaTeX'                        => 'mathjax-latex/mathjax-latex.php',
145
			'Enable Latex'                         => 'enable-latex/enable-latex.php',
146
			'WP QuickLaTeX'                        => 'wp-quicklatex/wp-quicklatex.php',
147
		),
148
		'protect'           => array(
149
			'Limit Login Attempts'                 => 'limit-login-attempts/limit-login-attempts.php',
150
			'Captcha'                              => 'captcha/captcha.php',
151
			'Brute Force Login Protection'         => 'brute-force-login-protection/brute-force-login-protection.php',
152
			'Login Security Solution'              => 'login-security-solution/login-security-solution.php',
153
			'WPSecureOps Brute Force Protect'      => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
154
			'BulletProof Security'                 => 'bulletproof-security/bulletproof-security.php',
155
			'SiteGuard WP Plugin'                  => 'siteguard/siteguard.php',
156
			'Security-protection'                  => 'security-protection/security-protection.php',
157
			'Login Security'                       => 'login-security/login-security.php',
158
			'Botnet Attack Blocker'                => 'botnet-attack-blocker/botnet-attack-blocker.php',
159
			'Wordfence Security'                   => 'wordfence/wordfence.php',
160
			'All In One WP Security & Firewall'    => 'all-in-one-wp-security-and-firewall/wp-security.php',
161
			'iThemes Security'                     => 'better-wp-security/better-wp-security.php',
162
		),
163
		'random-redirect'   => array(
164
			'Random Redirect 2'                    => 'random-redirect-2/random-redirect.php',
165
		),
166
		'related-posts'     => array(
167
			'YARPP'                                => 'yet-another-related-posts-plugin/yarpp.php',
168
			'WordPress Related Posts'              => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
169
			'nrelate Related Content'              => 'nrelate-related-content/nrelate-related.php',
170
			'Contextual Related Posts'             => 'contextual-related-posts/contextual-related-posts.php',
171
			'Related Posts for WordPress'          => 'microkids-related-posts/microkids-related-posts.php',
172
			'outbrain'                             => 'outbrain/outbrain.php',
173
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
174
			'Sexybookmarks'                        => 'sexybookmarks/shareaholic.php',
175
		),
176
		'sharedaddy'        => array(
177
			'AddThis'                              => 'addthis/addthis_social_widget.php',
178
			'Add To Any'                           => 'add-to-any/add-to-any.php',
179
			'ShareThis'                            => 'share-this/sharethis.php',
180
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
181
		),
182
		'seo-tools' => array(
183
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
184
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
185
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
186
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
187
			'The SEO Framework'                    => 'autodescription/autodescription.php',
188
		),
189
		'verification-tools' => array(
190
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
191
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
192
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
193
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
194
			'The SEO Framework'                    => 'autodescription/autodescription.php',
195
		),
196
		'widget-visibility' => array(
197
			'Widget Logic'                         => 'widget-logic/widget_logic.php',
198
			'Dynamic Widgets'                      => 'dynamic-widgets/dynamic-widgets.php',
199
		),
200
		'sitemaps' => array(
201
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
202
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
203
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
204
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
205
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
206
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
207
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
208
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
209
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
210
			'The SEO Framework'                    => 'autodescription/autodescription.php',
211
			'Sitemap'                              => 'sitemap/sitemap.php',
212
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
213
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
214
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
215
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
216
		),
217
		'lazy-images' => array(
218
			'Lazy Load'              => 'lazy-load/lazy-load.php',
219
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
220
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
221
		),
222
	);
223
224
	/**
225
	 * Plugins for which we turn off our Facebook OG Tags implementation.
226
	 *
227
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
228
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
229
	 *
230
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
231
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
232
	 */
233
	private $open_graph_conflicting_plugins = array(
234
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
235
		                                                         // 2 Click Social Media Buttons
236
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
237
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
238
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
239
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
240
		                                                         // Open Graph Meta Tags by Heateor
241
		'facebook/facebook.php',                                 // Facebook (official plugin)
242
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
243
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
244
		                                                         // Facebook Featured Image & OG Meta Tags
245
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
246
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
247
		                                                         // Facebook Open Graph Meta Tags for WordPress
248
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
249
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
250
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
251
		                                                         // Fedmich's Facebook Open Graph Meta
252
		'network-publisher/networkpub.php',                      // Network Publisher
253
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
254
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
255
		                                                         // NextScripts SNAP
256
		'og-tags/og-tags.php',                                   // OG Tags
257
		'opengraph/opengraph.php',                               // Open Graph
258
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
259
		                                                         // Open Graph Protocol Framework
260
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
261
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
262
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
263
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
264
		'sharepress/sharepress.php',                             // SharePress
265
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
266
		'social-discussions/social-discussions.php',             // Social Discussions
267
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
268
		'socialize/socialize.php',                               // Socialize
269
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
270
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
271
		                                                         // Tweet, Like, Google +1 and Share
272
		'wordbooker/wordbooker.php',                             // Wordbooker
273
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
274
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
275
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
276
		                                                         // WP Facebook Like Send & Open Graph Meta
277
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
278
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
279
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
280
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
281
		'open-graph-metabox/open-graph-metabox.php'              // Open Graph Metabox
282
	);
283
284
	/**
285
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
286
	 */
287
	private $twitter_cards_conflicting_plugins = array(
288
	//	'twitter/twitter.php',                       // The official one handles this on its own.
289
	//	                                             // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
290
		'eewee-twitter-card/index.php',              // Eewee Twitter Card
291
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
292
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
293
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
294
		                                             // Pure Web Brilliant's Social Graph Twitter Cards Extension
295
		'twitter-cards/twitter-cards.php',           // Twitter Cards
296
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
297
		'wp-to-twitter/wp-to-twitter.php',           // WP to Twitter
298
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
299
	);
300
301
	/**
302
	 * Message to display in admin_notice
303
	 * @var string
304
	 */
305
	public $message = '';
306
307
	/**
308
	 * Error to display in admin_notice
309
	 * @var string
310
	 */
311
	public $error = '';
312
313
	/**
314
	 * Modules that need more privacy description.
315
	 * @var string
316
	 */
317
	public $privacy_checks = '';
318
319
	/**
320
	 * Stats to record once the page loads
321
	 *
322
	 * @var array
323
	 */
324
	public $stats = array();
325
326
	/**
327
	 * Jetpack_Sync object
328
	 */
329
	public $sync;
330
331
	/**
332
	 * Verified data for JSON authorization request
333
	 */
334
	public $json_api_authorization_request = array();
335
336
	/**
337
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
338
	 */
339
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
340
341
	/**
342
	 * Holds the singleton instance of this class
343
	 * @since 2.3.3
344
	 * @var Jetpack
345
	 */
346
	static $instance = false;
347
348
	/**
349
	 * Singleton
350
	 * @static
351
	 */
352
	public static function init() {
353
		if ( ! self::$instance ) {
354
			self::$instance = new Jetpack;
355
356
			self::$instance->plugin_upgrade();
357
		}
358
359
		return self::$instance;
360
	}
361
362
	/**
363
	 * Must never be called statically
364
	 */
365
	function plugin_upgrade() {
366
		if ( Jetpack::is_active() ) {
367
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
368
			if ( JETPACK__VERSION != $version ) {
369
				// Prevent multiple upgrades at once - only a single process should trigger
370
				// an upgrade to avoid stampedes
371
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
372
					return;
373
				}
374
375
				// Set a short lock to prevent multiple instances of the upgrade
376
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
377
378
				// check which active modules actually exist and remove others from active_modules list
379
				$unfiltered_modules = Jetpack::get_active_modules();
380
				$modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
381
				if ( array_diff( $unfiltered_modules, $modules ) ) {
382
					Jetpack::update_active_modules( $modules );
383
				}
384
385
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
386
387
				// Upgrade to 4.3.0
388
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
389
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
390
				}
391
392
				// Make sure Markdown for posts gets turned back on
393
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
394
					update_option( 'wpcom_publish_posts_with_markdown', true );
395
				}
396
397
				if ( did_action( 'wp_loaded' ) ) {
398
					self::upgrade_on_load();
399
				} else {
400
					add_action(
401
						'wp_loaded',
402
						array( __CLASS__, 'upgrade_on_load' )
403
					);
404
				}
405
			}
406
		}
407
	}
408
409
	/**
410
	 * Runs upgrade routines that need to have modules loaded.
411
	 */
412
	static function upgrade_on_load() {
413
414
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
415
		// This can happen in case Jetpack has been just upgraded and is
416
		// being initialized late during the page load. In this case we wait
417
		// until the next proper admin page load with Jetpack active.
418
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
419
			delete_transient( self::$plugin_upgrade_lock_key );
420
421
			return;
422
		}
423
424
		Jetpack::maybe_set_version_option();
425
426
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
427
			Jetpack_Widget_Conditions::migrate_post_type_rules();
428
		}
429
430
		if (
431
			class_exists( 'Jetpack_Sitemap_Manager' )
432
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
433
		) {
434
			do_action( 'jetpack_sitemaps_purge_data' );
435
		}
436
437
		delete_transient( self::$plugin_upgrade_lock_key );
438
	}
439
440
	static function activate_manage( ) {
441
		if ( did_action( 'init' ) || current_filter() == 'init' ) {
442
			self::activate_module( 'manage', false, false );
443
		} else if ( !  has_action( 'init' , array( __CLASS__, 'activate_manage' ) ) ) {
444
			add_action( 'init', array( __CLASS__, 'activate_manage' ) );
445
		}
446
	}
447
448
	static function update_active_modules( $modules ) {
449
		$current_modules = Jetpack_Options::get_option( 'active_modules', array() );
450
451
		$success = Jetpack_Options::update_option( 'active_modules', array_unique( $modules ) );
452
453
		if ( is_array( $modules ) && is_array( $current_modules ) ) {
454
			$new_active_modules = array_diff( $modules, $current_modules );
455
			foreach( $new_active_modules as $module ) {
456
				/**
457
				 * Fires when a specific module is activated.
458
				 *
459
				 * @since 1.9.0
460
				 *
461
				 * @param string $module Module slug.
462
				 * @param boolean $success whether the module was activated. @since 4.2
463
				 */
464
				do_action( 'jetpack_activate_module', $module, $success );
465
466
				/**
467
				 * Fires when a module is activated.
468
				 * The dynamic part of the filter, $module, is the module slug.
469
				 *
470
				 * @since 1.9.0
471
				 *
472
				 * @param string $module Module slug.
473
				 */
474
				do_action( "jetpack_activate_module_$module", $module );
475
			}
476
477
			$new_deactive_modules = array_diff( $current_modules, $modules );
478
			foreach( $new_deactive_modules as $module ) {
479
				/**
480
				 * Fired after a module has been deactivated.
481
				 *
482
				 * @since 4.2.0
483
				 *
484
				 * @param string $module Module slug.
485
				 * @param boolean $success whether the module was deactivated.
486
				 */
487
				do_action( 'jetpack_deactivate_module', $module, $success );
488
				/**
489
				 * Fires when a module is deactivated.
490
				 * The dynamic part of the filter, $module, is the module slug.
491
				 *
492
				 * @since 1.9.0
493
				 *
494
				 * @param string $module Module slug.
495
				 */
496
				do_action( "jetpack_deactivate_module_$module", $module );
497
			}
498
		}
499
500
		return $success;
501
	}
502
503
	static function delete_active_modules() {
504
		self::update_active_modules( array() );
505
	}
506
507
	/**
508
	 * Constructor.  Initializes WordPress hooks
509
	 */
510
	private function __construct() {
511
		/*
512
		 * Check for and alert any deprecated hooks
513
		 */
514
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
515
516
		/*
517
		 * Enable enhanced handling of previewing sites in Calypso
518
		 */
519
		if ( Jetpack::is_active() ) {
520
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
521
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
522
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
523
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
524
		}
525
526
		/*
527
		 * Load things that should only be in Network Admin.
528
		 *
529
		 * For now blow away everything else until a more full
530
		 * understanding of what is needed at the network level is
531
		 * available
532
		 */
533
		if ( is_multisite() ) {
534
			Jetpack_Network::init();
535
		}
536
537
		/**
538
		 * Prepare Gutenberg Editor functionality
539
		 */
540
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
541
		Jetpack_Gutenberg::init();
542
		Jetpack_Gutenberg::load_independent_blocks();
543
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
544
545
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
546
547
		// Unlink user before deleting the user from .com
548
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
549
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
550
551
		// Alternate XML-RPC, via ?for=jetpack&jetpack=comms
552
		if ( isset( $_GET['jetpack'] ) && 'comms' == $_GET['jetpack'] && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
553
			if ( ! defined( 'XMLRPC_REQUEST' ) ) {
554
				define( 'XMLRPC_REQUEST', true );
555
			}
556
557
			add_action( 'template_redirect', array( $this, 'alternate_xmlrpc' ) );
558
559
			add_filter( 'xmlrpc_methods', array( $this, 'remove_non_jetpack_xmlrpc_methods' ), 1000 );
560
		}
561
562
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
563
			@ini_set( 'display_errors', false ); // Display errors can cause the XML to be not well formed.
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...
564
565
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
566
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
567
568
			$this->require_jetpack_authentication();
569
570
			if ( Jetpack::is_active() ) {
571
				// Hack to preserve $HTTP_RAW_POST_DATA
572
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
573
574
				$signed = $this->verify_xml_rpc_signature();
575 View Code Duplication
				if ( $signed && ! is_wp_error( $signed ) ) {
576
					// The actual API methods.
577
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
578
				} else {
579
					// The jetpack.authorize method should be available for unauthenticated users on a site with an
580
					// active Jetpack connection, so that additional users can link their account.
581
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'authorize_xmlrpc_methods' ) );
582
				}
583 View Code Duplication
			} else {
584
				// The bootstrap API methods.
585
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
586
				$signed = $this->verify_xml_rpc_signature();
587
				if ( $signed && ! is_wp_error( $signed ) ) {
588
					// the jetpack Provision method is available for blog-token-signed requests
589
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'provision_xmlrpc_methods' ) );
590
				}
591
			}
592
593
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
594
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
595
		} elseif (
596
			is_admin() &&
597
			isset( $_POST['action'] ) && (
598
				'jetpack_upload_file' == $_POST['action'] ||
599
				'jetpack_update_file' == $_POST['action']
600
			)
601
		) {
602
			$this->require_jetpack_authentication();
603
			$this->add_remote_request_handlers();
604
		} else {
605
			if ( Jetpack::is_active() ) {
606
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
607
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
608
			}
609
		}
610
611
		if ( Jetpack::is_active() ) {
612
			Jetpack_Heartbeat::init();
613
			if ( Jetpack::is_module_active( 'stats' ) && Jetpack::is_module_active( 'search' ) ) {
614
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
615
				Jetpack_Search_Performance_Logger::init();
616
			}
617
		}
618
619
		add_filter( 'determine_current_user', array( $this, 'wp_rest_authenticate' ) );
620
		add_filter( 'rest_authentication_errors', array( $this, 'wp_rest_authentication_errors' ) );
621
622
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
623
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
624
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
625
		}
626
627
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
628
629
		add_action( 'admin_init', array( $this, 'admin_init' ) );
630
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
631
632
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
633
634
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
635
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
636
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
637
638
		// returns HTTPS support status
639
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
640
641
		// If any module option is updated before Jump Start is dismissed, hide Jump Start.
642
		add_action( 'update_option', array( $this, 'jumpstart_has_updated_module_option' ) );
643
644
		// JITM AJAX callback function
645
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
646
647
		// Universal ajax callback for all tracking events triggered via js
648
		add_action( 'wp_ajax_jetpack_tracks', array( $this, 'jetpack_admin_ajax_tracks_callback' ) );
649
650
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
651
652
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
653
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
654
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
655
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
656
657
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
658
659
		/**
660
		 * These actions run checks to load additional files.
661
		 * They check for external files or plugins, so they need to run as late as possible.
662
		 */
663
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
664
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
665
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
666
667
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
668
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2  );
669
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
670
671
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
672
673
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
674
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
675
676
		// A filter to control all just in time messages
677
		add_filter( 'jetpack_just_in_time_msgs', '__return_true', 9 );
678
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9);
679
680
		// If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
681
		// We should make sure to only do this for front end links.
682
		if ( Jetpack::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
683
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
684
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
685
686
			//we'll override wp_notify_postauthor and wp_notify_moderator pluggable functions
687
			//so they point moderation links on emails to Calypso
688
			jetpack_require_lib( 'functions.wp-notify' );
689
		}
690
691
		// Update the Jetpack plan from API on heartbeats
692
		add_action( 'jetpack_heartbeat', array( $this, 'refresh_active_plan_from_wpcom' ) );
693
694
		/**
695
		 * This is the hack to concatenate all css files into one.
696
		 * For description and reasoning see the implode_frontend_css method
697
		 *
698
		 * Super late priority so we catch all the registered styles
699
		 */
700
		if( !is_admin() ) {
701
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
702
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
703
		}
704
705
		/**
706
		 * These are sync actions that we need to keep track of for jitms
707
		 */
708
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
709
710
		// Actually push the stats on shutdown.
711
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
712
			add_action( 'shutdown', array( $this, 'push_stats' ) );
713
		}
714
	}
715
716
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
717
		$post = get_post( $post_id );
718
719
		if ( empty( $post ) ) {
720
			return $default_url;
721
		}
722
723
		$post_type = $post->post_type;
724
725
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
726
		// https://en.support.wordpress.com/custom-post-types/
727
		$allowed_post_types = array(
728
			'post' => 'post',
729
			'page' => 'page',
730
			'jetpack-portfolio' => 'edit/jetpack-portfolio',
731
			'jetpack-testimonial' => 'edit/jetpack-testimonial',
732
		);
733
734
		if ( ! in_array( $post_type, array_keys( $allowed_post_types ) ) ) {
735
			return $default_url;
736
		}
737
738
		$path_prefix = $allowed_post_types[ $post_type ];
739
740
		$site_slug  = Jetpack::build_raw_urls( get_home_url() );
741
742
		return esc_url( sprintf( 'https://wordpress.com/%s/%s/%d', $path_prefix, $site_slug, $post_id ) );
743
	}
744
745
	function point_edit_comment_links_to_calypso( $url ) {
746
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
747
		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...
748
		return esc_url( sprintf( 'https://wordpress.com/comment/%s/%d',
749
			Jetpack::build_raw_urls( get_home_url() ),
750
			$query_args['amp;c']
751
		) );
752
	}
753
754
	function jetpack_track_last_sync_callback( $params ) {
755
		/**
756
		 * Filter to turn off jitm caching
757
		 *
758
		 * @since 5.4.0
759
		 *
760
		 * @param bool false Whether to cache just in time messages
761
		 */
762
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
763
			return $params;
764
		}
765
766
		if ( is_array( $params ) && isset( $params[0] ) ) {
767
			$option = $params[0];
768
			if ( 'active_plugins' === $option ) {
769
				// use the cache if we can, but not terribly important if it gets evicted
770
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
771
			}
772
		}
773
774
		return $params;
775
	}
776
777
	function jetpack_connection_banner_callback() {
778
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
779
780
		if ( isset( $_REQUEST['dismissBanner'] ) ) {
781
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
782
			wp_send_json_success();
783
		}
784
785
		wp_die();
786
	}
787
788
	/**
789
	 * Removes all XML-RPC methods that are not `jetpack.*`.
790
	 * Only used in our alternate XML-RPC endpoint, where we want to
791
	 * ensure that Core and other plugins' methods are not exposed.
792
	 *
793
	 * @param array $methods
794
	 * @return array filtered $methods
795
	 */
796
	function remove_non_jetpack_xmlrpc_methods( $methods ) {
797
		$jetpack_methods = array();
798
799
		foreach ( $methods as $method => $callback ) {
800
			if ( 0 === strpos( $method, 'jetpack.' ) ) {
801
				$jetpack_methods[ $method ] = $callback;
802
			}
803
		}
804
805
		return $jetpack_methods;
806
	}
807
808
	/**
809
	 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
810
	 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
811
	 * security/firewall policies, we provide our own alternate XML RPC API endpoint
812
	 * which is accessible via a different URI. Most of the below is copied directly
813
	 * from /xmlrpc.php so that we're replicating it as closely as possible.
814
	 */
815
	function alternate_xmlrpc() {
816
		// phpcs:disable PHPCompatibility.PHP.RemovedGlobalVariables.http_raw_post_dataDeprecatedRemoved
817
		global $HTTP_RAW_POST_DATA;
818
819
		// Some browser-embedded clients send cookies. We don't want them.
820
		$_COOKIE = array();
821
822
		// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
823
		// but we can do it ourself.
824
		if ( ! isset( $HTTP_RAW_POST_DATA ) ) {
825
			$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
826
		}
827
828
		// fix for mozBlog and other cases where '<?xml' isn't on the very first line
829
		if ( isset( $HTTP_RAW_POST_DATA ) ) {
830
			$HTTP_RAW_POST_DATA = trim( $HTTP_RAW_POST_DATA );
831
		}
832
833
		// phpcs:enable
834
835
		include_once( ABSPATH . 'wp-admin/includes/admin.php' );
836
		include_once( ABSPATH . WPINC . '/class-IXR.php' );
837
		include_once( ABSPATH . WPINC . '/class-wp-xmlrpc-server.php' );
838
839
		/**
840
		 * Filters the class used for handling XML-RPC requests.
841
		 *
842
		 * @since 3.1.0
843
		 *
844
		 * @param string $class The name of the XML-RPC server class.
845
		 */
846
		$wp_xmlrpc_server_class = apply_filters( 'wp_xmlrpc_server_class', 'wp_xmlrpc_server' );
847
		$wp_xmlrpc_server = new $wp_xmlrpc_server_class;
848
849
		// Fire off the request
850
		nocache_headers();
851
		$wp_xmlrpc_server->serve_request();
852
853
		exit;
854
	}
855
856
	function jetpack_admin_ajax_tracks_callback() {
857
		// Check for nonce
858
		if ( ! isset( $_REQUEST['tracksNonce'] ) || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' ) ) {
859
			wp_die( 'Permissions check failed.' );
860
		}
861
862
		if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] )  ) {
863
			wp_die( 'No valid event name or type.' );
864
		}
865
866
		$tracks_data = array();
867
		if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
868
			if ( is_array( $_REQUEST['tracksEventProp'] ) ) {
869
				$tracks_data = $_REQUEST['tracksEventProp'];
870
			} else {
871
				$tracks_data = array( 'clicked' => $_REQUEST['tracksEventProp'] );
872
			}
873
		}
874
875
		JetpackTracking::record_user_event( $_REQUEST['tracksEventName'], $tracks_data );
876
		wp_send_json_success();
877
		wp_die();
878
	}
879
880
	/**
881
	 * The callback for the JITM ajax requests.
882
	 */
883
	function jetpack_jitm_ajax_callback() {
884
		// Check for nonce
885
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
886
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
887
		}
888
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
889
			$module_slug = $_REQUEST['jitmModule'];
890
			Jetpack::log( 'activate', $module_slug );
891
			Jetpack::activate_module( $module_slug, false, false );
892
			Jetpack::state( 'message', 'no_message' );
893
894
			//A Jetpack module is being activated through a JITM, track it
895
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
896
			$this->do_stats( 'server_side' );
897
898
			wp_send_json_success();
899
		}
900
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
901
			// get the hide_jitm options array
902
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
903
			$module_slug = $_REQUEST['jitmModule'];
904
905
			if( ! $jetpack_hide_jitm ) {
906
				$jetpack_hide_jitm = array(
907
					$module_slug => 'hide'
908
				);
909
			} else {
910
				$jetpack_hide_jitm[$module_slug] = 'hide';
911
			}
912
913
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
914
915
			//jitm is being dismissed forever, track it
916
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
917
			$this->do_stats( 'server_side' );
918
919
			wp_send_json_success();
920
		}
921 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
922
			$module_slug = $_REQUEST['jitmModule'];
923
924
			// User went to WordPress.com, track this
925
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
926
			$this->do_stats( 'server_side' );
927
928
			wp_send_json_success();
929
		}
930 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'viewed' == $_REQUEST['jitmActionToTake'] ) {
931
			$track = $_REQUEST['jitmModule'];
932
933
			// User is viewing JITM, track it.
934
			$this->stat( 'jitm', $track . '-viewed-' . JETPACK__VERSION );
935
			$this->do_stats( 'server_side' );
936
937
			wp_send_json_success();
938
		}
939
	}
940
941
	/**
942
	 * If there are any stats that need to be pushed, but haven't been, push them now.
943
	 */
944
	function push_stats() {
945
		if ( ! empty( $this->stats ) ) {
946
			$this->do_stats( 'server_side' );
947
		}
948
	}
949
950
	function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
951
		switch( $cap ) {
952
			case 'jetpack_connect' :
953
			case 'jetpack_reconnect' :
954
				if ( Jetpack::is_development_mode() ) {
955
					$caps = array( 'do_not_allow' );
956
					break;
957
				}
958
				/**
959
				 * Pass through. If it's not development mode, these should match disconnect.
960
				 * Let users disconnect if it's development mode, just in case things glitch.
961
				 */
962
			case 'jetpack_disconnect' :
963
				/**
964
				 * In multisite, can individual site admins manage their own connection?
965
				 *
966
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
967
				 */
968
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
969
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
970
						/**
971
						 * We need to update the option name -- it's terribly unclear which
972
						 * direction the override goes.
973
						 *
974
						 * @todo: Update the option name to `sub-sites-can-manage-own-connections`
975
						 */
976
						$caps = array( 'do_not_allow' );
977
						break;
978
					}
979
				}
980
981
				$caps = array( 'manage_options' );
982
				break;
983
			case 'jetpack_manage_modules' :
984
			case 'jetpack_activate_modules' :
985
			case 'jetpack_deactivate_modules' :
986
				$caps = array( 'manage_options' );
987
				break;
988
			case 'jetpack_configure_modules' :
989
				$caps = array( 'manage_options' );
990
				break;
991
			case 'jetpack_manage_autoupdates' :
992
				$caps = array(
993
					'manage_options',
994
					'update_plugins',
995
				);
996
				break;
997
			case 'jetpack_network_admin_page':
998
			case 'jetpack_network_settings_page':
999
				$caps = array( 'manage_network_plugins' );
1000
				break;
1001
			case 'jetpack_network_sites_page':
1002
				$caps = array( 'manage_sites' );
1003
				break;
1004
			case 'jetpack_admin_page' :
1005
				if ( Jetpack::is_development_mode() ) {
1006
					$caps = array( 'manage_options' );
1007
					break;
1008
				} else {
1009
					$caps = array( 'read' );
1010
				}
1011
				break;
1012
			case 'jetpack_connect_user' :
1013
				if ( Jetpack::is_development_mode() ) {
1014
					$caps = array( 'do_not_allow' );
1015
					break;
1016
				}
1017
				$caps = array( 'read' );
1018
				break;
1019
		}
1020
		return $caps;
1021
	}
1022
1023
	function require_jetpack_authentication() {
1024
		// Don't let anyone authenticate
1025
		$_COOKIE = array();
1026
		remove_all_filters( 'authenticate' );
1027
		remove_all_actions( 'wp_login_failed' );
1028
1029
		if ( Jetpack::is_active() ) {
1030
			// Allow Jetpack authentication
1031
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
1032
		}
1033
	}
1034
1035
	/**
1036
	 * Load language files
1037
	 * @action plugins_loaded
1038
	 */
1039
	public static function plugin_textdomain() {
1040
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
1041
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
1042
	}
1043
1044
	/**
1045
	 * Register assets for use in various modules and the Jetpack admin page.
1046
	 *
1047
	 * @uses wp_script_is, wp_register_script, plugins_url
1048
	 * @action wp_loaded
1049
	 * @return null
1050
	 */
1051
	public function register_assets() {
1052
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
1053
			wp_register_script(
1054
				'spin',
1055
				self::get_file_url_for_environment( '_inc/build/spin.min.js', '_inc/spin.js' ),
1056
				false,
1057
				'1.3'
1058
			);
1059
		}
1060
1061
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
1062
			wp_register_script(
1063
				'jquery.spin',
1064
				self::get_file_url_for_environment( '_inc/build/jquery.spin.min.js', '_inc/jquery.spin.js' ),
1065
				array( 'jquery', 'spin' ),
1066
				'1.3'
1067
			);
1068
		}
1069
1070 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1071
			wp_register_script(
1072
				'jetpack-gallery-settings',
1073
				self::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1074
				array( 'media-views' ),
1075
				'20121225'
1076
			);
1077
		}
1078
1079
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1080
			wp_register_script(
1081
				'jetpack-twitter-timeline',
1082
				self::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1083
				array( 'jquery' ),
1084
				'4.0.0',
1085
				true
1086
			);
1087
		}
1088
1089
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1090
			wp_register_script(
1091
				'jetpack-facebook-embed',
1092
				self::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1093
				array( 'jquery' ),
1094
				null,
1095
				true
1096
			);
1097
1098
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1099
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1100
			if ( ! is_numeric( $fb_app_id ) ) {
1101
				$fb_app_id = '';
1102
			}
1103
			wp_localize_script(
1104
				'jetpack-facebook-embed',
1105
				'jpfbembed',
1106
				array(
1107
					'appid' => $fb_app_id,
1108
					'locale' => $this->get_locale(),
1109
				)
1110
			);
1111
		}
1112
1113
		/**
1114
		 * As jetpack_register_genericons is by default fired off a hook,
1115
		 * the hook may have already fired by this point.
1116
		 * So, let's just trigger it manually.
1117
		 */
1118
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
1119
		jetpack_register_genericons();
1120
1121
		/**
1122
		 * Register the social logos
1123
		 */
1124
		require_once( JETPACK__PLUGIN_DIR . '_inc/social-logos.php' );
1125
		jetpack_register_social_logos();
1126
1127 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
1128
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1129
	}
1130
1131
	/**
1132
	 * Guess locale from language code.
1133
	 *
1134
	 * @param string $lang Language code.
1135
	 * @return string|bool
1136
	 */
1137 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1138
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1139
			return 'en_US';
1140
		}
1141
1142
		if ( ! class_exists( 'GP_Locales' ) ) {
1143
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1144
				return false;
1145
			}
1146
1147
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1148
		}
1149
1150
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1151
			// WP.com: get_locale() returns 'it'
1152
			$locale = GP_Locales::by_slug( $lang );
1153
		} else {
1154
			// Jetpack: get_locale() returns 'it_IT';
1155
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1156
		}
1157
1158
		if ( ! $locale ) {
1159
			return false;
1160
		}
1161
1162
		if ( empty( $locale->facebook_locale ) ) {
1163
			if ( empty( $locale->wp_locale ) ) {
1164
				return false;
1165
			} else {
1166
				// Facebook SDK is smart enough to fall back to en_US if a
1167
				// locale isn't supported. Since supported Facebook locales
1168
				// can fall out of sync, we'll attempt to use the known
1169
				// wp_locale value and rely on said fallback.
1170
				return $locale->wp_locale;
1171
			}
1172
		}
1173
1174
		return $locale->facebook_locale;
1175
	}
1176
1177
	/**
1178
	 * Get the locale.
1179
	 *
1180
	 * @return string|bool
1181
	 */
1182
	function get_locale() {
1183
		$locale = $this->guess_locale_from_lang( get_locale() );
1184
1185
		if ( ! $locale ) {
1186
			$locale = 'en_US';
1187
		}
1188
1189
		return $locale;
1190
	}
1191
1192
	/**
1193
	 * Device Pixels support
1194
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
1195
	 */
1196
	function devicepx() {
1197
		if ( Jetpack::is_active() && ! Jetpack_AMP_Support::is_amp_request() ) {
1198
			wp_enqueue_script( 'devicepx', 'https://s0.wp.com/wp-content/js/devicepx-jetpack.js', array(), gmdate( 'oW' ), true );
1199
		}
1200
	}
1201
1202
	/**
1203
	 * Return the network_site_url so that .com knows what network this site is a part of.
1204
	 * @param  bool $option
1205
	 * @return string
1206
	 */
1207
	public function jetpack_main_network_site_option( $option ) {
1208
		return network_site_url();
1209
	}
1210
	/**
1211
	 * Network Name.
1212
	 */
1213
	static function network_name( $option = null ) {
1214
		global $current_site;
1215
		return $current_site->site_name;
1216
	}
1217
	/**
1218
	 * Does the network allow new user and site registrations.
1219
	 * @return string
1220
	 */
1221
	static function network_allow_new_registrations( $option = null ) {
1222
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
1223
	}
1224
	/**
1225
	 * Does the network allow admins to add new users.
1226
	 * @return boolian
1227
	 */
1228
	static function network_add_new_users( $option = null ) {
1229
		return (bool) get_site_option( 'add_new_users' );
1230
	}
1231
	/**
1232
	 * File upload psace left per site in MB.
1233
	 *  -1 means NO LIMIT.
1234
	 * @return number
1235
	 */
1236
	static function network_site_upload_space( $option = null ) {
1237
		// value in MB
1238
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1239
	}
1240
1241
	/**
1242
	 * Network allowed file types.
1243
	 * @return string
1244
	 */
1245
	static function network_upload_file_types( $option = null ) {
1246
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1247
	}
1248
1249
	/**
1250
	 * Maximum file upload size set by the network.
1251
	 * @return number
1252
	 */
1253
	static function network_max_upload_file_size( $option = null ) {
1254
		// value in KB
1255
		return get_site_option( 'fileupload_maxk', 300 );
1256
	}
1257
1258
	/**
1259
	 * Lets us know if a site allows admins to manage the network.
1260
	 * @return array
1261
	 */
1262
	static function network_enable_administration_menus( $option = null ) {
1263
		return get_site_option( 'menu_items' );
1264
	}
1265
1266
	/**
1267
	 * If a user has been promoted to or demoted from admin, we need to clear the
1268
	 * jetpack_other_linked_admins transient.
1269
	 *
1270
	 * @since 4.3.2
1271
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1272
	 *
1273
	 * @param int    $user_id   The user ID whose role changed.
1274
	 * @param string $role      The new role.
1275
	 * @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...
1276
	 */
1277
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1278
		if ( 'administrator' == $role
1279
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1280
			|| is_null( $old_roles )
1281
		) {
1282
			delete_transient( 'jetpack_other_linked_admins' );
1283
		}
1284
	}
1285
1286
	/**
1287
	 * Checks to see if there are any other users available to become primary
1288
	 * Users must both:
1289
	 * - Be linked to wpcom
1290
	 * - Be an admin
1291
	 *
1292
	 * @return mixed False if no other users are linked, Int if there are.
1293
	 */
1294
	static function get_other_linked_admins() {
1295
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1296
1297
		if ( false === $other_linked_users ) {
1298
			$admins = get_users( array( 'role' => 'administrator' ) );
1299
			if ( count( $admins ) > 1 ) {
1300
				$available = array();
1301
				foreach ( $admins as $admin ) {
1302
					if ( Jetpack::is_user_connected( $admin->ID ) ) {
1303
						$available[] = $admin->ID;
1304
					}
1305
				}
1306
1307
				$count_connected_admins = count( $available );
1308
				if ( count( $available ) > 1 ) {
1309
					$other_linked_users = $count_connected_admins;
1310
				} else {
1311
					$other_linked_users = 0;
1312
				}
1313
			} else {
1314
				$other_linked_users = 0;
1315
			}
1316
1317
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1318
		}
1319
1320
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1321
	}
1322
1323
	/**
1324
	 * Return whether we are dealing with a multi network setup or not.
1325
	 * The reason we are type casting this is because we want to avoid the situation where
1326
	 * the result is false since when is_main_network_option return false it cases
1327
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1328
	 * database which could be set to anything as opposed to what this function returns.
1329
	 * @param  bool  $option
1330
	 *
1331
	 * @return boolean
1332
	 */
1333
	public function is_main_network_option( $option ) {
1334
		// return '1' or ''
1335
		return (string) (bool) Jetpack::is_multi_network();
1336
	}
1337
1338
	/**
1339
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1340
	 *
1341
	 * @param  string  $option
1342
	 * @return boolean
1343
	 */
1344
	public function is_multisite( $option ) {
1345
		return (string) (bool) is_multisite();
1346
	}
1347
1348
	/**
1349
	 * Implemented since there is no core is multi network function
1350
	 * Right now there is no way to tell if we which network is the dominant network on the system
1351
	 *
1352
	 * @since  3.3
1353
	 * @return boolean
1354
	 */
1355
	public static function is_multi_network() {
1356
		global  $wpdb;
1357
1358
		// if we don't have a multi site setup no need to do any more
1359
		if ( ! is_multisite() ) {
1360
			return false;
1361
		}
1362
1363
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1364
		if ( $num_sites > 1 ) {
1365
			return true;
1366
		} else {
1367
			return false;
1368
		}
1369
	}
1370
1371
	/**
1372
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1373
	 * @return null
1374
	 */
1375
	function update_jetpack_main_network_site_option() {
1376
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1377
	}
1378
	/**
1379
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1380
	 *
1381
	 */
1382
	function update_jetpack_network_settings() {
1383
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1384
		// Only sync this info for the main network site.
1385
	}
1386
1387
	/**
1388
	 * Get back if the current site is single user site.
1389
	 *
1390
	 * @return bool
1391
	 */
1392
	public static function is_single_user_site() {
1393
		global $wpdb;
1394
1395 View Code Duplication
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1396
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1397
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1398
		}
1399
		return 1 === (int) $some_users;
1400
	}
1401
1402
	/**
1403
	 * Returns true if the site has file write access false otherwise.
1404
	 * @return string ( '1' | '0' )
1405
	 **/
1406
	public static function file_system_write_access() {
1407
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1408
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
1409
		}
1410
1411
		require_once( ABSPATH . 'wp-admin/includes/template.php' );
1412
1413
		$filesystem_method = get_filesystem_method();
1414
		if ( $filesystem_method === 'direct' ) {
1415
			return 1;
1416
		}
1417
1418
		ob_start();
1419
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1420
		ob_end_clean();
1421
		if ( $filesystem_credentials_are_stored ) {
1422
			return 1;
1423
		}
1424
		return 0;
1425
	}
1426
1427
	/**
1428
	 * Finds out if a site is using a version control system.
1429
	 * @return string ( '1' | '0' )
1430
	 **/
1431
	public static function is_version_controlled() {
1432
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Jetpack_Sync_Functions::is_version_controlled' );
1433
		return (string) (int) Jetpack_Sync_Functions::is_version_controlled();
1434
	}
1435
1436
	/**
1437
	 * Determines whether the current theme supports featured images or not.
1438
	 * @return string ( '1' | '0' )
1439
	 */
1440
	public static function featured_images_enabled() {
1441
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1442
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1443
	}
1444
1445
	/**
1446
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1447
	 *
1448
	 * @deprecated 4.7 use get_avatar_url instead.
1449
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1450
	 * @param int $size Size of the avatar image
1451
	 * @param string $default URL to a default image to use if no avatar is available
1452
	 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
1453
	 *
1454
	 * @return array
1455
	 */
1456
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1457
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1458
		return get_avatar_url( $id_or_email, array(
1459
			'size' => $size,
1460
			'default' => $default,
1461
			'force_default' => $force_display,
1462
		) );
1463
	}
1464
1465
	/**
1466
	 * jetpack_updates is saved in the following schema:
1467
	 *
1468
	 * array (
1469
	 *      'plugins'                       => (int) Number of plugin updates available.
1470
	 *      'themes'                        => (int) Number of theme updates available.
1471
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1472
	 *      'translations'                  => (int) Number of translation updates available.
1473
	 *      'total'                         => (int) Total of all available updates.
1474
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1475
	 * )
1476
	 * @return array
1477
	 */
1478
	public static function get_updates() {
1479
		$update_data = wp_get_update_data();
1480
1481
		// Stores the individual update counts as well as the total count.
1482
		if ( isset( $update_data['counts'] ) ) {
1483
			$updates = $update_data['counts'];
1484
		}
1485
1486
		// If we need to update WordPress core, let's find the latest version number.
1487 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1488
			$cur = get_preferred_from_update_core();
1489
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1490
				$updates['wp_update_version'] = $cur->current;
1491
			}
1492
		}
1493
		return isset( $updates ) ? $updates : array();
1494
	}
1495
1496
	public static function get_update_details() {
1497
		$update_details = array(
1498
			'update_core' => get_site_transient( 'update_core' ),
1499
			'update_plugins' => get_site_transient( 'update_plugins' ),
1500
			'update_themes' => get_site_transient( 'update_themes' ),
1501
		);
1502
		return $update_details;
1503
	}
1504
1505
	public static function refresh_update_data() {
1506
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1507
1508
	}
1509
1510
	public static function refresh_theme_data() {
1511
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1512
	}
1513
1514
	/**
1515
	 * Is Jetpack active?
1516
	 */
1517
	public static function is_active() {
1518
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1519
	}
1520
1521
	/**
1522
	 * Make an API call to WordPress.com for plan status
1523
	 *
1524
	 * @uses Jetpack_Options::get_option()
1525
	 * @uses Jetpack_Client::wpcom_json_api_request_as_blog()
1526
	 * @uses update_option()
1527
	 *
1528
	 * @access public
1529
	 * @static
1530
	 *
1531
	 * @return bool True if plan is updated, false if no update
1532
	 */
1533
	public static function refresh_active_plan_from_wpcom() {
1534
		// Make the API request
1535
		$request = sprintf( '/sites/%d', Jetpack_Options::get_option( 'id' ) );
1536
		$response = Jetpack_Client::wpcom_json_api_request_as_blog( $request, '1.1' );
1537
1538
		// Bail if there was an error or malformed response
1539
		if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
1540
			return false;
1541
		}
1542
1543
		// Decode the results
1544
		$results = json_decode( $response['body'], true );
1545
1546
		// Bail if there were no results or plan details returned
1547
		if ( ! is_array( $results ) || ! isset( $results['plan'] ) ) {
1548
			return false;
1549
		}
1550
1551
		// Store the option and return true if updated
1552
		return update_option( 'jetpack_active_plan', $results['plan'], true );
1553
	}
1554
1555
	/**
1556
	 * Get the plan that this Jetpack site is currently using
1557
	 *
1558
	 * @uses get_option()
1559
	 *
1560
	 * @access public
1561
	 * @static
1562
	 *
1563
	 * @return array Active Jetpack plan details
1564
	 */
1565
	public static function get_active_plan() {
1566
		global $active_plan_cache;
1567
1568
		// this can be expensive to compute so we cache for the duration of a request
1569
		if ( is_array( $active_plan_cache ) && ! empty( $active_plan_cache ) ) {
1570
			return $active_plan_cache;
1571
		}
1572
1573
		$plan = get_option( 'jetpack_active_plan', array() );
1574
1575
		// Set the default options
1576
		$plan = wp_parse_args( $plan, array(
1577
			'product_slug' => 'jetpack_free',
1578
			'class'        => 'free',
1579
			'features'     => array(
1580
				'active' => array()
1581
			),
1582
		) );
1583
1584
		$supports = array();
1585
1586
		// Define what paid modules are supported by personal plans
1587
		$personal_plans = array(
1588
			'jetpack_personal',
1589
			'jetpack_personal_monthly',
1590
			'personal-bundle',
1591
			'personal-bundle-2y',
1592
		);
1593
1594
		if ( in_array( $plan['product_slug'], $personal_plans ) ) {
1595
			// special support value, not a module but a separate plugin
1596
			$supports[] = 'akismet';
1597
			$plan['class'] = 'personal';
1598
		}
1599
1600
		// Define what paid modules are supported by premium plans
1601
		$premium_plans = array(
1602
			'jetpack_premium',
1603
			'jetpack_premium_monthly',
1604
			'value_bundle',
1605
			'value_bundle-2y',
1606
		);
1607
1608 View Code Duplication
		if ( in_array( $plan['product_slug'], $premium_plans ) ) {
1609
			$supports[] = 'akismet';
1610
			$supports[] = 'simple-payments';
1611
			$supports[] = 'vaultpress';
1612
			$supports[] = 'videopress';
1613
			$plan['class'] = 'premium';
1614
		}
1615
1616
		// Define what paid modules are supported by professional plans
1617
		$business_plans = array(
1618
			'jetpack_business',
1619
			'jetpack_business_monthly',
1620
			'business-bundle',
1621
			'business-bundle-2y',
1622
			'ecommerce-bundle',
1623
			'ecommerce-bundle-2y',
1624
			'vip',
1625
		);
1626
1627 View Code Duplication
		if ( in_array( $plan['product_slug'], $business_plans ) ) {
1628
			$supports[] = 'akismet';
1629
			$supports[] = 'simple-payments';
1630
			$supports[] = 'vaultpress';
1631
			$supports[] = 'videopress';
1632
			$plan['class'] = 'business';
1633
		}
1634
1635
		// get available features
1636
		foreach ( self::get_available_modules() as $module_slug ) {
1637
			$module = self::get_module( $module_slug );
1638
			if ( ! isset( $module ) || ! is_array( $module ) ) {
1639
				continue;
1640
			}
1641
			if ( in_array( 'free', $module['plan_classes'] ) || in_array( $plan['class'], $module['plan_classes'] ) ) {
1642
				$supports[] = $module_slug;
1643
			}
1644
		}
1645
1646
		$plan['supports'] = $supports;
1647
1648
		$active_plan_cache = $plan;
1649
1650
		return $plan;
1651
	}
1652
1653
	/**
1654
	 * Determine whether the active plan supports a particular feature
1655
	 *
1656
	 * @uses Jetpack::get_active_plan()
1657
	 *
1658
	 * @access public
1659
	 * @static
1660
	 *
1661
	 * @return bool True if plan supports feature, false if not
1662
	 */
1663
	public static function active_plan_supports( $feature ) {
1664
		$plan = Jetpack::get_active_plan();
1665
1666
		// Manually mapping WordPress.com features to Jetpack module slugs
1667
		foreach ( $plan['features']['active'] as $wpcom_feature ) {
1668
			switch ( $wpcom_feature ) {
1669
				case 'wordads-jetpack';
1670
1671
				// WordAds are supported for this site
1672
				if ( 'wordads' === $feature ) {
1673
					return true;
1674
				}
1675
				break;
1676
			}
1677
		}
1678
1679
		if (
1680
			in_array( $feature, $plan['supports'] )
1681
			|| in_array( $feature, $plan['features']['active'] )
1682
		) {
1683
			return true;
1684
		}
1685
1686
		return false;
1687
	}
1688
1689
	/**
1690
	 * Is Jetpack in development (offline) mode?
1691
	 */
1692
	public static function is_development_mode() {
1693
		$development_mode = false;
1694
1695
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1696
			$development_mode = JETPACK_DEV_DEBUG;
1697
		} elseif ( $site_url = site_url() ) {
1698
			$development_mode = false === strpos( $site_url, '.' );
1699
		}
1700
1701
		/**
1702
		 * Filters Jetpack's development mode.
1703
		 *
1704
		 * @see https://jetpack.com/support/development-mode/
1705
		 *
1706
		 * @since 2.2.1
1707
		 *
1708
		 * @param bool $development_mode Is Jetpack's development mode active.
1709
		 */
1710
		$development_mode = ( bool ) apply_filters( 'jetpack_development_mode', $development_mode );
1711
		return $development_mode;
1712
	}
1713
1714
	/**
1715
	 * Whether the site is currently onboarding or not.
1716
	 * A site is considered as being onboarded if it currently has an onboarding token.
1717
	 *
1718
	 * @since 5.8
1719
	 *
1720
	 * @access public
1721
	 * @static
1722
	 *
1723
	 * @return bool True if the site is currently onboarding, false otherwise
1724
	 */
1725
	public static function is_onboarding() {
1726
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1727
	}
1728
1729
	/**
1730
	* Get Jetpack development mode notice text and notice class.
1731
	*
1732
	* Mirrors the checks made in Jetpack::is_development_mode
1733
	*
1734
	*/
1735
	public static function show_development_mode_notice() {
1736
		if ( Jetpack::is_development_mode() ) {
1737
			if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1738
				$notice = sprintf(
1739
					/* translators: %s is a URL */
1740
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the JETPACK_DEV_DEBUG constant being defined in wp-config.php or elsewhere.', 'jetpack' ),
1741
					'https://jetpack.com/support/development-mode/'
1742
				);
1743
			} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1744
				$notice = sprintf(
1745
					/* translators: %s is a URL */
1746
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via site URL lacking a dot (e.g. http://localhost).', 'jetpack' ),
1747
					'https://jetpack.com/support/development-mode/'
1748
				);
1749
			} else {
1750
				$notice = sprintf(
1751
					/* translators: %s is a URL */
1752
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the jetpack_development_mode filter.', 'jetpack' ),
1753
					'https://jetpack.com/support/development-mode/'
1754
				);
1755
			}
1756
1757
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1758
		}
1759
1760
		// Throw up a notice if using a development version and as for feedback.
1761
		if ( Jetpack::is_development_version() ) {
1762
			/* translators: %s is a URL */
1763
			$notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), 'https://jetpack.com/contact-support/beta-group/' );
1764
1765
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1766
		}
1767
		// Throw up a notice if using staging mode
1768
		if ( Jetpack::is_staging_site() ) {
1769
			/* translators: %s is a URL */
1770
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.com/support/staging-sites/' );
1771
1772
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1773
		}
1774
	}
1775
1776
	/**
1777
	 * Whether Jetpack's version maps to a public release, or a development version.
1778
	 */
1779
	public static function is_development_version() {
1780
		/**
1781
		 * Allows filtering whether this is a development version of Jetpack.
1782
		 *
1783
		 * This filter is especially useful for tests.
1784
		 *
1785
		 * @since 4.3.0
1786
		 *
1787
		 * @param bool $development_version Is this a develoment version of Jetpack?
1788
		 */
1789
		return (bool) apply_filters(
1790
			'jetpack_development_version',
1791
			! preg_match( '/^\d+(\.\d+)+$/', Jetpack_Constants::get_constant( 'JETPACK__VERSION' ) )
1792
		);
1793
	}
1794
1795
	/**
1796
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1797
	 */
1798
	public static function is_user_connected( $user_id = false ) {
1799
		$user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
1800
		if ( ! $user_id ) {
1801
			return false;
1802
		}
1803
1804
		return (bool) Jetpack_Data::get_access_token( $user_id );
1805
	}
1806
1807
	/**
1808
	 * Get the wpcom user data of the current|specified connected user.
1809
	 */
1810
	public static function get_connected_user_data( $user_id = null ) {
1811
		if ( ! $user_id ) {
1812
			$user_id = get_current_user_id();
1813
		}
1814
1815
		$transient_key = "jetpack_connected_user_data_$user_id";
1816
1817
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1818
			return $cached_user_data;
1819
		}
1820
1821
		Jetpack::load_xml_rpc_client();
1822
		$xml = new Jetpack_IXR_Client( array(
1823
			'user_id' => $user_id,
1824
		) );
1825
		$xml->query( 'wpcom.getUser' );
1826
		if ( ! $xml->isError() ) {
1827
			$user_data = $xml->getResponse();
1828
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1829
			return $user_data;
1830
		}
1831
1832
		return false;
1833
	}
1834
1835
	/**
1836
	 * Get the wpcom email of the current|specified connected user.
1837
	 */
1838 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1839
		if ( ! $user_id ) {
1840
			$user_id = get_current_user_id();
1841
		}
1842
		Jetpack::load_xml_rpc_client();
1843
		$xml = new Jetpack_IXR_Client( array(
1844
			'user_id' => $user_id,
1845
		) );
1846
		$xml->query( 'wpcom.getUserEmail' );
1847
		if ( ! $xml->isError() ) {
1848
			return $xml->getResponse();
1849
		}
1850
		return false;
1851
	}
1852
1853
	/**
1854
	 * Get the wpcom email of the master user.
1855
	 */
1856
	public static function get_master_user_email() {
1857
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1858
		if ( $master_user_id ) {
1859
			return self::get_connected_user_email( $master_user_id );
1860
		}
1861
		return '';
1862
	}
1863
1864
	function current_user_is_connection_owner() {
1865
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1866
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1867
	}
1868
1869
	/**
1870
	 * Gets current user IP address.
1871
	 *
1872
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1873
	 *
1874
	 * @return string                  Current user IP address.
1875
	 */
1876
	public static function current_user_ip( $check_all_headers = false ) {
1877
		if ( $check_all_headers ) {
1878
			foreach ( array(
1879
				'HTTP_CF_CONNECTING_IP',
1880
				'HTTP_CLIENT_IP',
1881
				'HTTP_X_FORWARDED_FOR',
1882
				'HTTP_X_FORWARDED',
1883
				'HTTP_X_CLUSTER_CLIENT_IP',
1884
				'HTTP_FORWARDED_FOR',
1885
				'HTTP_FORWARDED',
1886
				'HTTP_VIA',
1887
			) as $key ) {
1888
				if ( ! empty( $_SERVER[ $key ] ) ) {
1889
					return $_SERVER[ $key ];
1890
				}
1891
			}
1892
		}
1893
1894
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1895
	}
1896
1897
	/**
1898
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1899
	 */
1900
	function extra_oembed_providers() {
1901
		// Cloudup: https://dev.cloudup.com/#oembed
1902
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1903
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1904
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1905
		wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true );
1906
		wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true );
1907
		wp_oembed_add_provider( '#https?://(www\.)?icloud\.com/keynote/.*#i', 'https://iwmb.icloud.com/iwmb/oembed', true );
1908
	}
1909
1910
	/**
1911
	 * Synchronize connected user role changes
1912
	 */
1913
	function user_role_change( $user_id ) {
1914
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Jetpack_Sync_Users::user_role_change()' );
1915
		Jetpack_Sync_Users::user_role_change( $user_id );
1916
	}
1917
1918
	/**
1919
	 * Loads the currently active modules.
1920
	 */
1921
	public static function load_modules() {
1922
		if (
1923
			! self::is_active()
1924
			&& ! self::is_development_mode()
1925
			&& ! self::is_onboarding()
1926
			&& (
1927
				! is_multisite()
1928
				|| ! get_site_option( 'jetpack_protect_active' )
1929
			)
1930
		) {
1931
			return;
1932
		}
1933
1934
		$version = Jetpack_Options::get_option( 'version' );
1935 View Code Duplication
		if ( ! $version ) {
1936
			$version = $old_version = JETPACK__VERSION . ':' . time();
1937
			/** This action is documented in class.jetpack.php */
1938
			do_action( 'updating_jetpack_version', $version, false );
1939
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1940
		}
1941
		list( $version ) = explode( ':', $version );
1942
1943
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1944
1945
		$modules_data = array();
1946
1947
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1948
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1949
			$updated_modules = array();
1950
			foreach ( $modules as $module ) {
1951
				$modules_data[ $module ] = Jetpack::get_module( $module );
1952
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1953
					continue;
1954
				}
1955
1956
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1957
					continue;
1958
				}
1959
1960
				$updated_modules[] = $module;
1961
			}
1962
1963
			$modules = array_diff( $modules, $updated_modules );
1964
		}
1965
1966
		$is_development_mode = Jetpack::is_development_mode();
1967
1968
		foreach ( $modules as $index => $module ) {
1969
			// If we're in dev mode, disable modules requiring a connection
1970
			if ( $is_development_mode ) {
1971
				// Prime the pump if we need to
1972
				if ( empty( $modules_data[ $module ] ) ) {
1973
					$modules_data[ $module ] = Jetpack::get_module( $module );
1974
				}
1975
				// If the module requires a connection, but we're in local mode, don't include it.
1976
				if ( $modules_data[ $module ]['requires_connection'] ) {
1977
					continue;
1978
				}
1979
			}
1980
1981
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1982
				continue;
1983
			}
1984
1985
			if ( ! include_once( Jetpack::get_module_path( $module ) ) ) {
1986
				unset( $modules[ $index ] );
1987
				self::update_active_modules( array_values( $modules ) );
1988
				continue;
1989
			}
1990
1991
			/**
1992
			 * Fires when a specific module is loaded.
1993
			 * The dynamic part of the hook, $module, is the module slug.
1994
			 *
1995
			 * @since 1.1.0
1996
			 */
1997
			do_action( 'jetpack_module_loaded_' . $module );
1998
		}
1999
2000
		/**
2001
		 * Fires when all the modules are loaded.
2002
		 *
2003
		 * @since 1.1.0
2004
		 */
2005
		do_action( 'jetpack_modules_loaded' );
2006
2007
		// 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.
2008
		require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
2009
	}
2010
2011
	/**
2012
	 * Check if Jetpack's REST API compat file should be included
2013
	 * @action plugins_loaded
2014
	 * @return null
2015
	 */
2016
	public function check_rest_api_compat() {
2017
		/**
2018
		 * Filters the list of REST API compat files to be included.
2019
		 *
2020
		 * @since 2.2.5
2021
		 *
2022
		 * @param array $args Array of REST API compat files to include.
2023
		 */
2024
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
2025
2026
		if ( function_exists( 'bbpress' ) )
2027
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
2028
2029
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
2030
			require_once $_jetpack_rest_api_compat_include;
2031
	}
2032
2033
	/**
2034
	 * Gets all plugins currently active in values, regardless of whether they're
2035
	 * traditionally activated or network activated.
2036
	 *
2037
	 * @todo Store the result in core's object cache maybe?
2038
	 */
2039
	public static function get_active_plugins() {
2040
		$active_plugins = (array) get_option( 'active_plugins', array() );
2041
2042
		if ( is_multisite() ) {
2043
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
2044
			// whereas active_plugins stores them in the values.
2045
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
2046
			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...
2047
				$active_plugins = array_merge( $active_plugins, $network_plugins );
2048
			}
2049
		}
2050
2051
		sort( $active_plugins );
2052
2053
		return array_unique( $active_plugins );
2054
	}
2055
2056
	/**
2057
	 * Gets and parses additional plugin data to send with the heartbeat data
2058
	 *
2059
	 * @since 3.8.1
2060
	 *
2061
	 * @return array Array of plugin data
2062
	 */
2063
	public static function get_parsed_plugin_data() {
2064
		if ( ! function_exists( 'get_plugins' ) ) {
2065
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
2066
		}
2067
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
2068
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
2069
		$active_plugins = Jetpack::get_active_plugins();
2070
2071
		$plugins = array();
2072
		foreach ( $all_plugins as $path => $plugin_data ) {
2073
			$plugins[ $path ] = array(
2074
					'is_active' => in_array( $path, $active_plugins ),
2075
					'file'      => $path,
2076
					'name'      => $plugin_data['Name'],
2077
					'version'   => $plugin_data['Version'],
2078
					'author'    => $plugin_data['Author'],
2079
			);
2080
		}
2081
2082
		return $plugins;
2083
	}
2084
2085
	/**
2086
	 * Gets and parses theme data to send with the heartbeat data
2087
	 *
2088
	 * @since 3.8.1
2089
	 *
2090
	 * @return array Array of theme data
2091
	 */
2092
	public static function get_parsed_theme_data() {
2093
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
2094
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2095
2096
		$themes = array();
2097
		foreach ( $all_themes as $slug => $theme_data ) {
2098
			$theme_headers = array();
2099
			foreach ( $header_keys as $header_key ) {
2100
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2101
			}
2102
2103
			$themes[ $slug ] = array(
2104
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
2105
					'slug' => $slug,
2106
					'theme_root' => $theme_data->get_theme_root_uri(),
2107
					'parent' => $theme_data->parent(),
2108
					'headers' => $theme_headers
2109
			);
2110
		}
2111
2112
		return $themes;
2113
	}
2114
2115
	/**
2116
	 * Checks whether a specific plugin is active.
2117
	 *
2118
	 * We don't want to store these in a static variable, in case
2119
	 * there are switch_to_blog() calls involved.
2120
	 */
2121
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2122
		return in_array( $plugin, self::get_active_plugins() );
2123
	}
2124
2125
	/**
2126
	 * Check if Jetpack's Open Graph tags should be used.
2127
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2128
	 *
2129
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2130
	 * @action plugins_loaded
2131
	 * @return null
2132
	 */
2133
	public function check_open_graph() {
2134
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
2135
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2136
		}
2137
2138
		$active_plugins = self::get_active_plugins();
2139
2140
		if ( ! empty( $active_plugins ) ) {
2141
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2142
				if ( in_array( $plugin, $active_plugins ) ) {
2143
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2144
					break;
2145
				}
2146
			}
2147
		}
2148
2149
		/**
2150
		 * Allow the addition of Open Graph Meta Tags to all pages.
2151
		 *
2152
		 * @since 2.0.3
2153
		 *
2154
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2155
		 */
2156
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2157
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2158
		}
2159
	}
2160
2161
	/**
2162
	 * Check if Jetpack's Twitter tags should be used.
2163
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2164
	 *
2165
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2166
	 * @action plugins_loaded
2167
	 * @return null
2168
	 */
2169
	public function check_twitter_tags() {
2170
2171
		$active_plugins = self::get_active_plugins();
2172
2173
		if ( ! empty( $active_plugins ) ) {
2174
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2175
				if ( in_array( $plugin, $active_plugins ) ) {
2176
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2177
					break;
2178
				}
2179
			}
2180
		}
2181
2182
		/**
2183
		 * Allow Twitter Card Meta tags to be disabled.
2184
		 *
2185
		 * @since 2.6.0
2186
		 *
2187
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2188
		 */
2189
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2190
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2191
		}
2192
	}
2193
2194
	/**
2195
	 * Allows plugins to submit security reports.
2196
 	 *
2197
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
2198
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
2199
	 * @param array   $args         See definitions above
2200
	 */
2201
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2202
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2203
	}
2204
2205
/* Jetpack Options API */
2206
2207
	public static function get_option_names( $type = 'compact' ) {
2208
		return Jetpack_Options::get_option_names( $type );
2209
	}
2210
2211
	/**
2212
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2213
 	 *
2214
	 * @param string $name    Option name
2215
	 * @param mixed  $default (optional)
2216
	 */
2217
	public static function get_option( $name, $default = false ) {
2218
		return Jetpack_Options::get_option( $name, $default );
2219
	}
2220
2221
	/**
2222
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2223
 	 *
2224
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2225
	 * @param string $name  Option name
2226
	 * @param mixed  $value Option value
2227
	 */
2228
	public static function update_option( $name, $value ) {
2229
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2230
		return Jetpack_Options::update_option( $name, $value );
2231
	}
2232
2233
	/**
2234
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2235
 	 *
2236
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2237
	 * @param array $array array( option name => option value, ... )
2238
	 */
2239
	public static function update_options( $array ) {
2240
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2241
		return Jetpack_Options::update_options( $array );
2242
	}
2243
2244
	/**
2245
	 * Deletes the given option.  May be passed multiple option names as an array.
2246
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2247
	 *
2248
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2249
	 * @param string|array $names
2250
	 */
2251
	public static function delete_option( $names ) {
2252
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2253
		return Jetpack_Options::delete_option( $names );
2254
	}
2255
2256
	/**
2257
	 * Enters a user token into the user_tokens option
2258
	 *
2259
	 * @param int $user_id
2260
	 * @param string $token
2261
	 * return bool
2262
	 */
2263
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2264
		// not designed for concurrent updates
2265
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
2266
		if ( ! is_array( $user_tokens ) )
2267
			$user_tokens = array();
2268
		$user_tokens[$user_id] = $token;
2269
		if ( $is_master_user ) {
2270
			$master_user = $user_id;
2271
			$options     = compact( 'user_tokens', 'master_user' );
2272
		} else {
2273
			$options = compact( 'user_tokens' );
2274
		}
2275
		return Jetpack_Options::update_options( $options );
2276
	}
2277
2278
	/**
2279
	 * Returns an array of all PHP files in the specified absolute path.
2280
	 * Equivalent to glob( "$absolute_path/*.php" ).
2281
	 *
2282
	 * @param string $absolute_path The absolute path of the directory to search.
2283
	 * @return array Array of absolute paths to the PHP files.
2284
	 */
2285
	public static function glob_php( $absolute_path ) {
2286
		if ( function_exists( 'glob' ) ) {
2287
			return glob( "$absolute_path/*.php" );
2288
		}
2289
2290
		$absolute_path = untrailingslashit( $absolute_path );
2291
		$files = array();
2292
		if ( ! $dir = @opendir( $absolute_path ) ) {
2293
			return $files;
2294
		}
2295
2296
		while ( false !== $file = readdir( $dir ) ) {
2297
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2298
				continue;
2299
			}
2300
2301
			$file = "$absolute_path/$file";
2302
2303
			if ( ! is_file( $file ) ) {
2304
				continue;
2305
			}
2306
2307
			$files[] = $file;
2308
		}
2309
2310
		closedir( $dir );
2311
2312
		return $files;
2313
	}
2314
2315
	public static function activate_new_modules( $redirect = false ) {
2316
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
2317
			return;
2318
		}
2319
2320
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2321 View Code Duplication
		if ( ! $jetpack_old_version ) {
2322
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2323
			/** This action is documented in class.jetpack.php */
2324
			do_action( 'updating_jetpack_version', $version, false );
2325
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2326
		}
2327
2328
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2329
2330
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2331
			return;
2332
		}
2333
2334
		$active_modules     = Jetpack::get_active_modules();
2335
		$reactivate_modules = array();
2336
		foreach ( $active_modules as $active_module ) {
2337
			$module = Jetpack::get_module( $active_module );
2338
			if ( ! isset( $module['changed'] ) ) {
2339
				continue;
2340
			}
2341
2342
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2343
				continue;
2344
			}
2345
2346
			$reactivate_modules[] = $active_module;
2347
			Jetpack::deactivate_module( $active_module );
2348
		}
2349
2350
		$new_version = JETPACK__VERSION . ':' . time();
2351
		/** This action is documented in class.jetpack.php */
2352
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2353
		Jetpack_Options::update_options(
2354
			array(
2355
				'version'     => $new_version,
2356
				'old_version' => $jetpack_old_version,
2357
			)
2358
		);
2359
2360
		Jetpack::state( 'message', 'modules_activated' );
2361
		Jetpack::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules );
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...
2362
2363
		if ( $redirect ) {
2364
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2365
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2366
				$page = $_GET['page'];
2367
			}
2368
2369
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
2370
			exit;
2371
		}
2372
	}
2373
2374
	/**
2375
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2376
	 * Make sure to tuck away module "library" files in a sub-directory.
2377
	 */
2378
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2379
		static $modules = null;
2380
2381
		if ( ! isset( $modules ) ) {
2382
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2383
			// Use the cache if we're on the front-end and it's available...
2384
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2385
				$modules = $available_modules_option[ JETPACK__VERSION ];
2386
			} else {
2387
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2388
2389
				$modules = array();
2390
2391
				foreach ( $files as $file ) {
2392
					if ( ! $headers = Jetpack::get_module( $file ) ) {
2393
						continue;
2394
					}
2395
2396
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
2397
				}
2398
2399
				Jetpack_Options::update_option( 'available_modules', array(
2400
					JETPACK__VERSION => $modules,
2401
				) );
2402
			}
2403
		}
2404
2405
		/**
2406
		 * Filters the array of modules available to be activated.
2407
		 *
2408
		 * @since 2.4.0
2409
		 *
2410
		 * @param array $modules Array of available modules.
2411
		 * @param string $min_version Minimum version number required to use modules.
2412
		 * @param string $max_version Maximum version number required to use modules.
2413
		 */
2414
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
2415
2416
		if ( ! $min_version && ! $max_version ) {
2417
			return array_keys( $mods );
2418
		}
2419
2420
		$r = array();
2421
		foreach ( $mods as $slug => $introduced ) {
2422
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2423
				continue;
2424
			}
2425
2426
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2427
				continue;
2428
			}
2429
2430
			$r[] = $slug;
2431
		}
2432
2433
		return $r;
2434
	}
2435
2436
	/**
2437
	 * Default modules loaded on activation.
2438
	 */
2439
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2440
		$return = array();
2441
2442
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
2443
			$module_data = Jetpack::get_module( $module );
2444
2445
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2446
				case 'yes' :
2447
					$return[] = $module;
2448
					break;
2449
				case 'public' :
2450
					if ( Jetpack_Options::get_option( 'public' ) ) {
2451
						$return[] = $module;
2452
					}
2453
					break;
2454
				case 'no' :
2455
				default :
2456
					break;
2457
			}
2458
		}
2459
		/**
2460
		 * Filters the array of default modules.
2461
		 *
2462
		 * @since 2.5.0
2463
		 *
2464
		 * @param array $return Array of default modules.
2465
		 * @param string $min_version Minimum version number required to use modules.
2466
		 * @param string $max_version Maximum version number required to use modules.
2467
		 */
2468
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
2469
	}
2470
2471
	/**
2472
	 * Checks activated modules during auto-activation to determine
2473
	 * if any of those modules are being deprecated.  If so, close
2474
	 * them out, and add any replacement modules.
2475
	 *
2476
	 * Runs at priority 99 by default.
2477
	 *
2478
	 * This is run late, so that it can still activate a module if
2479
	 * the new module is a replacement for another that the user
2480
	 * currently has active, even if something at the normal priority
2481
	 * would kibosh everything.
2482
	 *
2483
	 * @since 2.6
2484
	 * @uses jetpack_get_default_modules filter
2485
	 * @param array $modules
2486
	 * @return array
2487
	 */
2488
	function handle_deprecated_modules( $modules ) {
2489
		$deprecated_modules = array(
2490
			'debug'            => null,  // Closed out and moved to ./class.jetpack-debugger.php
2491
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2492
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2493
		);
2494
2495
		// Don't activate SSO if they never completed activating WPCC.
2496
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
2497
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2498
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2499
				$deprecated_modules['wpcc'] = null;
2500
			}
2501
		}
2502
2503
		foreach ( $deprecated_modules as $module => $replacement ) {
2504
			if ( Jetpack::is_module_active( $module ) ) {
2505
				self::deactivate_module( $module );
2506
				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...
2507
					$modules[] = $replacement;
2508
				}
2509
			}
2510
		}
2511
2512
		return array_unique( $modules );
2513
	}
2514
2515
	/**
2516
	 * Checks activated plugins during auto-activation to determine
2517
	 * if any of those plugins are in the list with a corresponding module
2518
	 * that is not compatible with the plugin. The module will not be allowed
2519
	 * to auto-activate.
2520
	 *
2521
	 * @since 2.6
2522
	 * @uses jetpack_get_default_modules filter
2523
	 * @param array $modules
2524
	 * @return array
2525
	 */
2526
	function filter_default_modules( $modules ) {
2527
2528
		$active_plugins = self::get_active_plugins();
2529
2530
		if ( ! empty( $active_plugins ) ) {
2531
2532
			// For each module we'd like to auto-activate...
2533
			foreach ( $modules as $key => $module ) {
2534
				// If there are potential conflicts for it...
2535
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2536
					// For each potential conflict...
2537
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2538
						// If that conflicting plugin is active...
2539
						if ( in_array( $plugin, $active_plugins ) ) {
2540
							// Remove that item from being auto-activated.
2541
							unset( $modules[ $key ] );
2542
						}
2543
					}
2544
				}
2545
			}
2546
		}
2547
2548
		return $modules;
2549
	}
2550
2551
	/**
2552
	 * Extract a module's slug from its full path.
2553
	 */
2554
	public static function get_module_slug( $file ) {
2555
		return str_replace( '.php', '', basename( $file ) );
2556
	}
2557
2558
	/**
2559
	 * Generate a module's path from its slug.
2560
	 */
2561
	public static function get_module_path( $slug ) {
2562
		return JETPACK__PLUGIN_DIR . "modules/$slug.php";
2563
	}
2564
2565
	/**
2566
	 * Load module data from module file. Headers differ from WordPress
2567
	 * plugin headers to avoid them being identified as standalone
2568
	 * plugins on the WordPress plugins page.
2569
	 */
2570
	public static function get_module( $module ) {
2571
		$headers = array(
2572
			'name'                      => 'Module Name',
2573
			'description'               => 'Module Description',
2574
			'jumpstart_desc'            => 'Jumpstart Description',
2575
			'sort'                      => 'Sort Order',
2576
			'recommendation_order'      => 'Recommendation Order',
2577
			'introduced'                => 'First Introduced',
2578
			'changed'                   => 'Major Changes In',
2579
			'deactivate'                => 'Deactivate',
2580
			'free'                      => 'Free',
2581
			'requires_connection'       => 'Requires Connection',
2582
			'auto_activate'             => 'Auto Activate',
2583
			'module_tags'               => 'Module Tags',
2584
			'feature'                   => 'Feature',
2585
			'additional_search_queries' => 'Additional Search Queries',
2586
			'plan_classes'              => 'Plans',
2587
		);
2588
2589
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2590
2591
		$mod = Jetpack::get_file_data( $file, $headers );
2592
		if ( empty( $mod['name'] ) ) {
2593
			return false;
2594
		}
2595
2596
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2597
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2598
		$mod['deactivate']              = empty( $mod['deactivate'] );
2599
		$mod['free']                    = empty( $mod['free'] );
2600
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2601
2602
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2603
			$mod['auto_activate'] = 'No';
2604
		} else {
2605
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2606
		}
2607
2608
		if ( $mod['module_tags'] ) {
2609
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2610
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2611
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2612
		} else {
2613
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2614
		}
2615
2616 View Code Duplication
		if ( $mod['plan_classes'] ) {
2617
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2618
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2619
		} else {
2620
			$mod['plan_classes'] = array( 'free' );
2621
		}
2622
2623 View Code Duplication
		if ( $mod['feature'] ) {
2624
			$mod['feature'] = explode( ',', $mod['feature'] );
2625
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2626
		} else {
2627
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2628
		}
2629
2630
		/**
2631
		 * Filters the feature array on a module.
2632
		 *
2633
		 * This filter allows you to control where each module is filtered: Recommended,
2634
		 * Jumpstart, and the default "Other" listing.
2635
		 *
2636
		 * @since 3.5.0
2637
		 *
2638
		 * @param array   $mod['feature'] The areas to feature this module:
2639
		 *     'Jumpstart' adds to the "Jumpstart" option to activate many modules at once.
2640
		 *     'Recommended' shows on the main Jetpack admin screen.
2641
		 *     'Other' should be the default if no other value is in the array.
2642
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2643
		 * @param array   $mod All the currently assembled module data.
2644
		 */
2645
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2646
2647
		/**
2648
		 * Filter the returned data about a module.
2649
		 *
2650
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2651
		 * so please be careful.
2652
		 *
2653
		 * @since 3.6.0
2654
		 *
2655
		 * @param array   $mod    The details of the requested module.
2656
		 * @param string  $module The slug of the module, e.g. sharedaddy
2657
		 * @param string  $file   The path to the module source file.
2658
		 */
2659
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2660
	}
2661
2662
	/**
2663
	 * Like core's get_file_data implementation, but caches the result.
2664
	 */
2665
	public static function get_file_data( $file, $headers ) {
2666
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2667
		$file_name = basename( $file );
2668
2669
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2670
2671
		$file_data_option = get_transient( $cache_key );
2672
2673
		if ( false === $file_data_option ) {
2674
			$file_data_option = array();
2675
		}
2676
2677
		$key           = md5( $file_name . serialize( $headers ) );
2678
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2679
2680
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2681
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2682
			return $file_data_option[ $key ];
2683
		}
2684
2685
		$data = get_file_data( $file, $headers );
2686
2687
		$file_data_option[ $key ] = $data;
2688
2689
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2690
2691
		return $data;
2692
	}
2693
2694
2695
	/**
2696
	 * Return translated module tag.
2697
	 *
2698
	 * @param string $tag Tag as it appears in each module heading.
2699
	 *
2700
	 * @return mixed
2701
	 */
2702
	public static function translate_module_tag( $tag ) {
2703
		return jetpack_get_module_i18n_tag( $tag );
2704
	}
2705
2706
	/**
2707
	 * Get i18n strings as a JSON-encoded string
2708
	 *
2709
	 * @return string The locale as JSON
2710
	 */
2711
	public static function get_i18n_data_json() {
2712
2713
		// WordPress 5.0 uses md5 hashes of file paths to associate translation
2714
		// JSON files with the file they should be included for. This is an md5
2715
		// of '_inc/build/admin.js'.
2716
		$path_md5 = '1bac79e646a8bf4081a5011ab72d5807';
2717
2718
		$i18n_json =
2719
				   JETPACK__PLUGIN_DIR
2720
				   . 'languages/json/jetpack-'
2721
				   . get_user_locale()
2722
				   . '-'
2723
				   . $path_md5
2724
				   . '.json';
2725
2726
		if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
2727
			$locale_data = @file_get_contents( $i18n_json );
2728
			if ( $locale_data ) {
2729
				return $locale_data;
2730
			}
2731
		}
2732
2733
		// Return valid empty Jed locale
2734
		return json_encode( array(
2735
			'' => array(
2736
				'domain' => 'jetpack',
2737
				'lang'   => is_admin() ? get_user_locale() : get_locale(),
2738
			),
2739
		) );
2740
	}
2741
2742
	/**
2743
	 * Add locale data setup to wp-i18n
2744
	 *
2745
	 * Any Jetpack script that depends on wp-i18n should use this method to set up the locale.
2746
	 *
2747
	 * The locale setup depends on an adding inline script. This is error-prone and could easily
2748
	 * result in multiple additions of the same script when exactly 0 or 1 is desireable.
2749
	 *
2750
	 * This method provides a safe way to request the setup multiple times but add the script at
2751
	 * most once.
2752
	 *
2753
	 * @since 6.7.0
2754
	 *
2755
	 * @return void
2756
	 */
2757
	public static function setup_wp_i18n_locale_data() {
2758
		static $script_added = false;
2759
		if ( ! $script_added ) {
2760
			$script_added = true;
2761
			wp_add_inline_script(
2762
				'wp-i18n',
2763
				'wp.i18n.setLocaleData( ' . Jetpack::get_i18n_data_json() . ', \'jetpack\' );'
2764
			);
2765
		}
2766
	}
2767
2768
	/**
2769
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2770
	 *
2771
	 * @since 3.9.2
2772
	 *
2773
	 * @param array $modules
2774
	 *
2775
	 * @return string|void
2776
	 */
2777
	public static function get_translated_modules( $modules ) {
2778
		foreach ( $modules as $index => $module ) {
2779
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2780
			if ( isset( $module['name'] ) ) {
2781
				$modules[ $index ]['name'] = $i18n_module['name'];
2782
			}
2783
			if ( isset( $module['description'] ) ) {
2784
				$modules[ $index ]['description'] = $i18n_module['description'];
2785
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2786
			}
2787
		}
2788
		return $modules;
2789
	}
2790
2791
	/**
2792
	 * Get a list of activated modules as an array of module slugs.
2793
	 */
2794
	public static function get_active_modules() {
2795
		$active = Jetpack_Options::get_option( 'active_modules' );
2796
2797
		if ( ! is_array( $active ) ) {
2798
			$active = array();
2799
		}
2800
2801
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2802
			$active[] = 'vaultpress';
2803
		} else {
2804
			$active = array_diff( $active, array( 'vaultpress' ) );
2805
		}
2806
2807
		//If protect is active on the main site of a multisite, it should be active on all sites.
2808
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2809
			$active[] = 'protect';
2810
		}
2811
2812
		/**
2813
		 * Allow filtering of the active modules.
2814
		 *
2815
		 * Gives theme and plugin developers the power to alter the modules that
2816
		 * are activated on the fly.
2817
		 *
2818
		 * @since 5.8.0
2819
		 *
2820
		 * @param array $active Array of active module slugs.
2821
		 */
2822
		$active = apply_filters( 'jetpack_active_modules', $active );
2823
2824
		return array_unique( $active );
2825
	}
2826
2827
	/**
2828
	 * Check whether or not a Jetpack module is active.
2829
	 *
2830
	 * @param string $module The slug of a Jetpack module.
2831
	 * @return bool
2832
	 *
2833
	 * @static
2834
	 */
2835
	public static function is_module_active( $module ) {
2836
		return in_array( $module, self::get_active_modules() );
2837
	}
2838
2839
	public static function is_module( $module ) {
2840
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2841
	}
2842
2843
	/**
2844
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2845
	 *
2846
	 * @param bool $catch True to start catching, False to stop.
2847
	 *
2848
	 * @static
2849
	 */
2850
	public static function catch_errors( $catch ) {
2851
		static $display_errors, $error_reporting;
2852
2853
		if ( $catch ) {
2854
			$display_errors  = @ini_set( 'display_errors', 1 );
2855
			$error_reporting = @error_reporting( E_ALL );
2856
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2857
		} else {
2858
			@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...
2859
			@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...
2860
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2861
		}
2862
	}
2863
2864
	/**
2865
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2866
	 */
2867
	public static function catch_errors_on_shutdown() {
2868
		Jetpack::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2869
	}
2870
2871
	/**
2872
	 * Rewrite any string to make paths easier to read.
2873
	 *
2874
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2875
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2876
	 *
2877
	 * @param $string
2878
	 * @return mixed
2879
	 */
2880
	public static function alias_directories( $string ) {
2881
		// ABSPATH has a trailing slash.
2882
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2883
		// WP_CONTENT_DIR does not have a trailing slash.
2884
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2885
2886
		return $string;
2887
	}
2888
2889
	public static function activate_default_modules(
2890
		$min_version = false,
2891
		$max_version = false,
2892
		$other_modules = array(),
2893
		$redirect = true,
2894
		$send_state_messages = true
2895
	) {
2896
		$jetpack = Jetpack::init();
2897
2898
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2899
		$modules = array_merge( $other_modules, $modules );
2900
2901
		// Look for standalone plugins and disable if active.
2902
2903
		$to_deactivate = array();
2904
		foreach ( $modules as $module ) {
2905
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2906
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2907
			}
2908
		}
2909
2910
		$deactivated = array();
2911
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2912
			list( $probable_file, $probable_title ) = $deactivate_me;
2913
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2914
				$deactivated[] = $module;
2915
			}
2916
		}
2917
2918
		if ( $deactivated && $redirect ) {
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...
2919
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2920
2921
			$url = add_query_arg(
2922
				array(
2923
					'action'   => 'activate_default_modules',
2924
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2925
				),
2926
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2927
			);
2928
			wp_safe_redirect( $url );
2929
			exit;
2930
		}
2931
2932
		/**
2933
		 * Fires before default modules are activated.
2934
		 *
2935
		 * @since 1.9.0
2936
		 *
2937
		 * @param string $min_version Minimum version number required to use modules.
2938
		 * @param string $max_version Maximum version number required to use modules.
2939
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2940
		 */
2941
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2942
2943
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2944
		if ( $send_state_messages ) {
2945
			Jetpack::restate();
2946
			Jetpack::catch_errors( true );
2947
		}
2948
2949
		$active = Jetpack::get_active_modules();
2950
2951
		foreach ( $modules as $module ) {
2952
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2953
				$active[] = $module;
2954
				self::update_active_modules( $active );
2955
				continue;
2956
			}
2957
2958
			if ( $send_state_messages && in_array( $module, $active ) ) {
2959
				$module_info = Jetpack::get_module( $module );
2960 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2961
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2962
					if ( $active_state = Jetpack::state( $state ) ) {
2963
						$active_state = explode( ',', $active_state );
2964
					} else {
2965
						$active_state = array();
2966
					}
2967
					$active_state[] = $module;
2968
					Jetpack::state( $state, implode( ',', $active_state ) );
2969
				}
2970
				continue;
2971
			}
2972
2973
			$file = Jetpack::get_module_path( $module );
2974
			if ( ! file_exists( $file ) ) {
2975
				continue;
2976
			}
2977
2978
			// we'll override this later if the plugin can be included without fatal error
2979
			if ( $redirect ) {
2980
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2981
			}
2982
2983
			if ( $send_state_messages ) {
2984
				Jetpack::state( 'error', 'module_activation_failed' );
2985
				Jetpack::state( 'module', $module );
2986
			}
2987
2988
			ob_start();
2989
			require_once $file;
2990
2991
			$active[] = $module;
2992
2993 View Code Duplication
			if ( $send_state_messages ) {
2994
2995
				$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2996
				if ( $active_state = Jetpack::state( $state ) ) {
2997
					$active_state = explode( ',', $active_state );
2998
				} else {
2999
					$active_state = array();
3000
				}
3001
				$active_state[] = $module;
3002
				Jetpack::state( $state, implode( ',', $active_state ) );
3003
			}
3004
3005
			Jetpack::update_active_modules( $active );
3006
3007
			ob_end_clean();
3008
		}
3009
3010
		if ( $send_state_messages ) {
3011
			Jetpack::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...
3012
			Jetpack::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...
3013
		}
3014
3015
		Jetpack::catch_errors( false );
3016
		/**
3017
		 * Fires when default modules are activated.
3018
		 *
3019
		 * @since 1.9.0
3020
		 *
3021
		 * @param string $min_version Minimum version number required to use modules.
3022
		 * @param string $max_version Maximum version number required to use modules.
3023
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
3024
		 */
3025
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
3026
	}
3027
3028
	public static function activate_module( $module, $exit = true, $redirect = true ) {
3029
		/**
3030
		 * Fires before a module is activated.
3031
		 *
3032
		 * @since 2.6.0
3033
		 *
3034
		 * @param string $module Module slug.
3035
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
3036
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
3037
		 */
3038
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
3039
3040
		$jetpack = Jetpack::init();
3041
3042
		if ( ! strlen( $module ) )
3043
			return false;
3044
3045
		if ( ! Jetpack::is_module( $module ) )
3046
			return false;
3047
3048
		// If it's already active, then don't do it again
3049
		$active = Jetpack::get_active_modules();
3050
		foreach ( $active as $act ) {
3051
			if ( $act == $module )
3052
				return true;
3053
		}
3054
3055
		$module_data = Jetpack::get_module( $module );
3056
3057
		if ( ! Jetpack::is_active() ) {
3058
			if ( ! Jetpack::is_development_mode() && ! Jetpack::is_onboarding() )
3059
				return false;
3060
3061
			// If we're not connected but in development mode, make sure the module doesn't require a connection
3062
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
3063
				return false;
3064
		}
3065
3066
		// Check and see if the old plugin is active
3067
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
3068
			// Deactivate the old plugin
3069
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
3070
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
3071
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
3072
				Jetpack::state( 'deactivated_plugins', $module );
3073
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
3074
				exit;
3075
			}
3076
		}
3077
3078
		// Protect won't work with mis-configured IPs
3079
		if ( 'protect' === $module ) {
3080
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
3081
			if ( ! jetpack_protect_get_ip() ) {
3082
				Jetpack::state( 'message', 'protect_misconfigured_ip' );
3083
				return false;
3084
			}
3085
		}
3086
3087
		if ( ! Jetpack::active_plan_supports( $module ) ) {
3088
			return false;
3089
		}
3090
3091
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
3092
		Jetpack::state( 'module', $module );
3093
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
3094
3095
		Jetpack::catch_errors( true );
3096
		ob_start();
3097
		require Jetpack::get_module_path( $module );
3098
		/** This action is documented in class.jetpack.php */
3099
		do_action( 'jetpack_activate_module', $module );
3100
		$active[] = $module;
3101
		Jetpack::update_active_modules( $active );
3102
3103
		Jetpack::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...
3104
		ob_end_clean();
3105
		Jetpack::catch_errors( false );
3106
3107
		// A flag for Jump Start so it's not shown again. Only set if it hasn't been yet.
3108 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
3109
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
3110
3111
			//Jump start is being dismissed send data to MC Stats
3112
			$jetpack->stat( 'jumpstart', 'manual,'.$module );
3113
3114
			$jetpack->do_stats( 'server_side' );
3115
		}
3116
3117
		if ( $redirect ) {
3118
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3119
		}
3120
		if ( $exit ) {
3121
			exit;
3122
		}
3123
		return true;
3124
	}
3125
3126
	function activate_module_actions( $module ) {
3127
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3128
	}
3129
3130
	public static function deactivate_module( $module ) {
3131
		/**
3132
		 * Fires when a module is deactivated.
3133
		 *
3134
		 * @since 1.9.0
3135
		 *
3136
		 * @param string $module Module slug.
3137
		 */
3138
		do_action( 'jetpack_pre_deactivate_module', $module );
3139
3140
		$jetpack = Jetpack::init();
3141
3142
		$active = Jetpack::get_active_modules();
3143
		$new    = array_filter( array_diff( $active, (array) $module ) );
3144
3145
		// A flag for Jump Start so it's not shown again.
3146 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
3147
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
3148
3149
			//Jump start is being dismissed send data to MC Stats
3150
			$jetpack->stat( 'jumpstart', 'manual,deactivated-'.$module );
3151
3152
			$jetpack->do_stats( 'server_side' );
3153
		}
3154
3155
		return self::update_active_modules( $new );
3156
	}
3157
3158
	public static function enable_module_configurable( $module ) {
3159
		$module = Jetpack::get_module_slug( $module );
3160
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3161
	}
3162
3163
	/**
3164
	 * Composes a module configure URL. It uses Jetpack settings search as default value
3165
	 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3166
	 *
3167
	 * @param string $module Module slug
3168
	 * @return string $url module configuration URL
3169
	 */
3170
	public static function module_configuration_url( $module ) {
3171
		$module = Jetpack::get_module_slug( $module );
3172
		$default_url =  Jetpack::admin_url() . "#/settings?term=$module";
3173
		/**
3174
		 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3175
		 *
3176
		 * @since 6.9.0
3177
		 *
3178
		 * @param string $default_url Default url, which redirects to jetpack settings page.
3179
		 */
3180
		$url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3181
3182
		return $url;
3183
	}
3184
3185
	public static function module_configuration_load( $module, $method ) {
3186
		$module = Jetpack::get_module_slug( $module );
3187
		add_action( 'jetpack_module_configuration_load_' . $module, $method );
3188
	}
3189
3190
	public static function module_configuration_head( $module, $method ) {
3191
		$module = Jetpack::get_module_slug( $module );
3192
		add_action( 'jetpack_module_configuration_head_' . $module, $method );
3193
	}
3194
3195
	public static function module_configuration_screen( $module, $method ) {
3196
		$module = Jetpack::get_module_slug( $module );
3197
		add_action( 'jetpack_module_configuration_screen_' . $module, $method );
3198
	}
3199
3200
	public static function module_configuration_activation_screen( $module, $method ) {
3201
		$module = Jetpack::get_module_slug( $module );
3202
		add_action( 'display_activate_module_setting_' . $module, $method );
3203
	}
3204
3205
/* Installation */
3206
3207
	public static function bail_on_activation( $message, $deactivate = true ) {
3208
?>
3209
<!doctype html>
3210
<html>
3211
<head>
3212
<meta charset="<?php bloginfo( 'charset' ); ?>">
3213
<style>
3214
* {
3215
	text-align: center;
3216
	margin: 0;
3217
	padding: 0;
3218
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3219
}
3220
p {
3221
	margin-top: 1em;
3222
	font-size: 18px;
3223
}
3224
</style>
3225
<body>
3226
<p><?php echo esc_html( $message ); ?></p>
3227
</body>
3228
</html>
3229
<?php
3230
		if ( $deactivate ) {
3231
			$plugins = get_option( 'active_plugins' );
3232
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3233
			$update  = false;
3234
			foreach ( $plugins as $i => $plugin ) {
3235
				if ( $plugin === $jetpack ) {
3236
					$plugins[$i] = false;
3237
					$update = true;
3238
				}
3239
			}
3240
3241
			if ( $update ) {
3242
				update_option( 'active_plugins', array_filter( $plugins ) );
3243
			}
3244
		}
3245
		exit;
3246
	}
3247
3248
	/**
3249
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3250
	 * @static
3251
	 */
3252
	public static function plugin_activation( $network_wide ) {
3253
		Jetpack_Options::update_option( 'activated', 1 );
3254
3255
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3256
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3257
		}
3258
3259
		if ( $network_wide )
3260
			Jetpack::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...
3261
3262
		// For firing one-off events (notices) immediately after activation
3263
		set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS );
3264
3265
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3266
3267
		Jetpack::plugin_initialize();
3268
	}
3269
3270
	public static function get_activation_source( $referer_url ) {
3271
3272
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3273
			return array( 'wp-cli', null );
3274
		}
3275
3276
		$referer = parse_url( $referer_url );
3277
3278
		$source_type = 'unknown';
3279
		$source_query = null;
3280
3281
		if ( ! is_array( $referer ) ) {
3282
			return array( $source_type, $source_query );
3283
		}
3284
3285
		$plugins_path = parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
3286
		$plugins_install_path = parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
3287
3288
		if ( isset( $referer['query'] ) ) {
3289
			parse_str( $referer['query'], $query_parts );
3290
		} else {
3291
			$query_parts = array();
3292
		}
3293
3294
		if ( $plugins_path === $referer['path'] ) {
3295
			$source_type = 'list';
3296
		} elseif ( $plugins_install_path === $referer['path'] ) {
3297
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3298
			switch( $tab ) {
3299
				case 'popular':
3300
					$source_type = 'popular';
3301
					break;
3302
				case 'recommended':
3303
					$source_type = 'recommended';
3304
					break;
3305
				case 'favorites':
3306
					$source_type = 'favorites';
3307
					break;
3308
				case 'search':
3309
					$source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3310
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3311
					break;
3312
				default:
3313
					$source_type = 'featured';
3314
			}
3315
		}
3316
3317
		return array( $source_type, $source_query );
3318
	}
3319
3320
	/**
3321
	 * Runs before bumping version numbers up to a new version
3322
	 * @param  string $version    Version:timestamp
3323
	 * @param  string $old_version Old Version:timestamp or false if not set yet.
3324
	 * @return null              [description]
3325
	 */
3326
	public static function do_version_bump( $version, $old_version ) {
3327
		if ( ! $old_version ) { // For new sites
3328
			// Setting up jetpack manage
3329
			Jetpack::activate_manage();
3330
		} else {
3331
			// If a Jetpack is still active but not connected when updating verion, remind them to connect with the banner.
3332
			if ( ! Jetpack::is_active() ) {
3333
				Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3334
			}
3335
		}
3336
	}
3337
3338
	/**
3339
	 * Sets the internal version number and activation state.
3340
	 * @static
3341
	 */
3342
	public static function plugin_initialize() {
3343
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3344
			Jetpack_Options::update_option( 'activated', 2 );
3345
		}
3346
3347 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3348
			$version = $old_version = JETPACK__VERSION . ':' . time();
3349
			/** This action is documented in class.jetpack.php */
3350
			do_action( 'updating_jetpack_version', $version, false );
3351
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3352
		}
3353
3354
		Jetpack::load_modules();
3355
3356
		Jetpack_Options::delete_option( 'do_activate' );
3357
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3358
	}
3359
3360
	/**
3361
	 * Removes all connection options
3362
	 * @static
3363
	 */
3364
	public static function plugin_deactivation( ) {
3365
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
3366
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3367
			Jetpack_Network::init()->deactivate();
3368
		} else {
3369
			Jetpack::disconnect( false );
3370
			//Jetpack_Heartbeat::init()->deactivate();
3371
		}
3372
	}
3373
3374
	/**
3375
	 * Disconnects from the Jetpack servers.
3376
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3377
	 * @static
3378
	 */
3379
	public static function disconnect( $update_activated_state = true ) {
3380
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3381
		Jetpack::clean_nonces( true );
3382
3383
		// If the site is in an IDC because sync is not allowed,
3384
		// let's make sure to not disconnect the production site.
3385
		if ( ! self::validate_sync_error_idc_option() ) {
3386
			JetpackTracking::record_user_event( 'disconnect_site', array() );
3387
			Jetpack::load_xml_rpc_client();
3388
			$xml = new Jetpack_IXR_Client();
3389
			$xml->query( 'jetpack.deregister' );
3390
		}
3391
3392
		Jetpack_Options::delete_option(
3393
			array(
3394
				'blog_token',
3395
				'user_token',
3396
				'user_tokens',
3397
				'master_user',
3398
				'time_diff',
3399
				'fallback_no_verify_ssl_certs',
3400
			)
3401
		);
3402
3403
		Jetpack_IDC::clear_all_idc_options();
3404
		Jetpack_Options::delete_raw_option( 'jetpack_secrets' );
3405
3406
		if ( $update_activated_state ) {
3407
			Jetpack_Options::update_option( 'activated', 4 );
3408
		}
3409
3410
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3411
			// Check then record unique disconnection if site has never been disconnected previously
3412
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3413
				$jetpack_unique_connection['disconnected'] = 1;
3414
			} else {
3415
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3416
					//track unique disconnect
3417
					$jetpack = Jetpack::init();
3418
3419
					$jetpack->stat( 'connections', 'unique-disconnect' );
3420
					$jetpack->do_stats( 'server_side' );
3421
				}
3422
				// increment number of times disconnected
3423
				$jetpack_unique_connection['disconnected'] += 1;
3424
			}
3425
3426
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3427
		}
3428
3429
		// Delete cached connected user data
3430
		$transient_key = "jetpack_connected_user_data_" . get_current_user_id();
3431
		delete_transient( $transient_key );
3432
3433
		// Delete all the sync related data. Since it could be taking up space.
3434
		require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
3435
		Jetpack_Sync_Sender::get_instance()->uninstall();
3436
3437
		// Disable the Heartbeat cron
3438
		Jetpack_Heartbeat::init()->deactivate();
3439
	}
3440
3441
	/**
3442
	 * Unlinks the current user from the linked WordPress.com user
3443
	 */
3444
	public static function unlink_user( $user_id = null ) {
3445
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
3446
			return false;
3447
3448
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
3449
3450
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
3451
			return false;
3452
3453
		if ( ! isset( $tokens[ $user_id ] ) )
3454
			return false;
3455
3456
		Jetpack::load_xml_rpc_client();
3457
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
3458
		$xml->query( 'jetpack.unlink_user', $user_id );
3459
3460
		unset( $tokens[ $user_id ] );
3461
3462
		Jetpack_Options::update_option( 'user_tokens', $tokens );
3463
3464
		/**
3465
		 * Fires after the current user has been unlinked from WordPress.com.
3466
		 *
3467
		 * @since 4.1.0
3468
		 *
3469
		 * @param int $user_id The current user's ID.
3470
		 */
3471
		do_action( 'jetpack_unlinked_user', $user_id );
3472
3473
		return true;
3474
	}
3475
3476
	/**
3477
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3478
	 */
3479
	public static function try_registration() {
3480
		// The user has agreed to the TOS at some point by now.
3481
		Jetpack_Options::update_option( 'tos_agreed', true );
3482
3483
		// Let's get some testing in beta versions and such.
3484
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3485
			// Before attempting to connect, let's make sure that the domains are viable.
3486
			$domains_to_check = array_unique( array(
3487
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
3488
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
3489
			) );
3490
			foreach ( $domains_to_check as $domain ) {
3491
				$result = Jetpack_Data::is_usable_domain( $domain );
3492
				if ( is_wp_error( $result ) ) {
3493
					return $result;
3494
				}
3495
			}
3496
		}
3497
3498
		$result = Jetpack::register();
3499
3500
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3501
		if ( ! $result || is_wp_error( $result ) ) {
3502
			return $result;
3503
		} else {
3504
			return true;
3505
		}
3506
	}
3507
3508
	/**
3509
	 * Tracking an internal event log. Try not to put too much chaff in here.
3510
	 *
3511
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3512
	 */
3513
	public static function log( $code, $data = null ) {
3514
		// only grab the latest 200 entries
3515
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3516
3517
		// Append our event to the log
3518
		$log_entry = array(
3519
			'time'    => time(),
3520
			'user_id' => get_current_user_id(),
3521
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3522
			'code'    => $code,
3523
		);
3524
		// Don't bother storing it unless we've got some.
3525
		if ( ! is_null( $data ) ) {
3526
			$log_entry['data'] = $data;
3527
		}
3528
		$log[] = $log_entry;
3529
3530
		// Try add_option first, to make sure it's not autoloaded.
3531
		// @todo: Add an add_option method to Jetpack_Options
3532
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3533
			Jetpack_Options::update_option( 'log', $log );
3534
		}
3535
3536
		/**
3537
		 * Fires when Jetpack logs an internal event.
3538
		 *
3539
		 * @since 3.0.0
3540
		 *
3541
		 * @param array $log_entry {
3542
		 *	Array of details about the log entry.
3543
		 *
3544
		 *	@param string time Time of the event.
3545
		 *	@param int user_id ID of the user who trigerred the event.
3546
		 *	@param int blog_id Jetpack Blog ID.
3547
		 *	@param string code Unique name for the event.
3548
		 *	@param string data Data about the event.
3549
		 * }
3550
		 */
3551
		do_action( 'jetpack_log_entry', $log_entry );
3552
	}
3553
3554
	/**
3555
	 * Get the internal event log.
3556
	 *
3557
	 * @param $event (string) - only return the specific log events
3558
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3559
	 *
3560
	 * @return array of log events || WP_Error for invalid params
3561
	 */
3562
	public static function get_log( $event = false, $num = false ) {
3563
		if ( $event && ! is_string( $event ) ) {
3564
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
3565
		}
3566
3567
		if ( $num && ! is_numeric( $num ) ) {
3568
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
3569
		}
3570
3571
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3572
3573
		// If nothing set - act as it did before, otherwise let's start customizing the output
3574
		if ( ! $num && ! $event ) {
3575
			return $entire_log;
3576
		} else {
3577
			$entire_log = array_reverse( $entire_log );
3578
		}
3579
3580
		$custom_log_output = array();
3581
3582
		if ( $event ) {
3583
			foreach ( $entire_log as $log_event ) {
3584
				if ( $event == $log_event[ 'code' ] ) {
3585
					$custom_log_output[] = $log_event;
3586
				}
3587
			}
3588
		} else {
3589
			$custom_log_output = $entire_log;
3590
		}
3591
3592
		if ( $num ) {
3593
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3594
		}
3595
3596
		return $custom_log_output;
3597
	}
3598
3599
	/**
3600
	 * Log modification of important settings.
3601
	 */
3602
	public static function log_settings_change( $option, $old_value, $value ) {
3603
		switch( $option ) {
3604
			case 'jetpack_sync_non_public_post_stati':
3605
				self::log( $option, $value );
3606
				break;
3607
		}
3608
	}
3609
3610
	/**
3611
	 * Return stat data for WPCOM sync
3612
	 */
3613
	public static function get_stat_data( $encode = true, $extended = true ) {
3614
		$data = Jetpack_Heartbeat::generate_stats_array();
3615
3616
		if ( $extended ) {
3617
			$additional_data = self::get_additional_stat_data();
3618
			$data = array_merge( $data, $additional_data );
3619
		}
3620
3621
		if ( $encode ) {
3622
			return json_encode( $data );
3623
		}
3624
3625
		return $data;
3626
	}
3627
3628
	/**
3629
	 * Get additional stat data to sync to WPCOM
3630
	 */
3631
	public static function get_additional_stat_data( $prefix = '' ) {
3632
		$return["{$prefix}themes"]         = Jetpack::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...
3633
		$return["{$prefix}plugins-extra"]  = Jetpack::get_parsed_plugin_data();
3634
		$return["{$prefix}users"]          = (int) Jetpack::get_site_user_count();
3635
		$return["{$prefix}site-count"]     = 0;
3636
3637
		if ( function_exists( 'get_blog_count' ) ) {
3638
			$return["{$prefix}site-count"] = get_blog_count();
3639
		}
3640
		return $return;
3641
	}
3642
3643
	private static function get_site_user_count() {
3644
		global $wpdb;
3645
3646
		if ( function_exists( 'wp_is_large_network' ) ) {
3647
			if ( wp_is_large_network( 'users' ) ) {
3648
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3649
			}
3650
		}
3651 View Code Duplication
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3652
			// It wasn't there, so regenerate the data and save the transient
3653
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3654
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3655
		}
3656
		return $user_count;
3657
	}
3658
3659
	/* Admin Pages */
3660
3661
	function admin_init() {
3662
		// If the plugin is not connected, display a connect message.
3663
		if (
3664
			// the plugin was auto-activated and needs its candy
3665
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3666
		||
3667
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3668
			! Jetpack_Options::get_option( 'activated' )
3669
		) {
3670
			Jetpack::plugin_initialize();
3671
		}
3672
3673
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
3674
			Jetpack_Connection_Banner::init();
3675
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3676
			// Upgrade: 1.1 -> 1.1.1
3677
			// Check and see if host can verify the Jetpack servers' SSL certificate
3678
			$args = array();
3679
			Jetpack_Client::_wp_remote_request(
3680
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
3681
				$args,
3682
				true
3683
			);
3684
		} else if ( $this->can_display_jetpack_manage_notice() && ! Jetpack_Options::get_option( 'dismissed_manage_banner' ) ) {
3685
			// Show the notice on the Dashboard only for now
3686
			add_action( 'load-index.php', array( $this, 'prepare_manage_jetpack_notice' ) );
3687
		}
3688
3689
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3690
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3691
		}
3692
3693
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3694
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3695
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3696
3697
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
3698
			// Artificially throw errors in certain whitelisted cases during plugin activation
3699
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3700
		}
3701
3702
		// Jetpack Manage Activation Screen from .com
3703
		Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
3704
3705
		// Add custom column in wp-admin/users.php to show whether user is linked.
3706
		add_filter( 'manage_users_columns',       array( $this, 'jetpack_icon_user_connected' ) );
3707
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3708
		add_action( 'admin_print_styles',         array( $this, 'jetpack_user_col_style' ) );
3709
	}
3710
3711
	function admin_body_class( $admin_body_class = '' ) {
3712
		$classes = explode( ' ', trim( $admin_body_class ) );
3713
3714
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3715
3716
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3717
		return " $admin_body_class ";
3718
	}
3719
3720
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3721
		return $admin_body_class . ' jetpack-pagestyles ';
3722
	}
3723
3724
	/**
3725
	 * Call this function if you want the Big Jetpack Manage Notice to show up.
3726
	 *
3727
	 * @return null
3728
	 */
3729
	function prepare_manage_jetpack_notice() {
3730
3731
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3732
		add_action( 'admin_notices', array( $this, 'admin_jetpack_manage_notice' ) );
3733
	}
3734
3735
	function manage_activate_screen() {
3736
		include ( JETPACK__PLUGIN_DIR . 'modules/manage/activate-admin.php' );
3737
	}
3738
	/**
3739
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3740
	 * This function artificially throws errors for such cases (whitelisted).
3741
	 *
3742
	 * @param string $plugin The activated plugin.
3743
	 */
3744
	function throw_error_on_activate_plugin( $plugin ) {
3745
		$active_modules = Jetpack::get_active_modules();
3746
3747
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3748
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3749
			$throw = false;
3750
3751
			// Try and make sure it really was the stats plugin
3752
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3753
				if ( 'stats.php' == basename( $plugin ) ) {
3754
					$throw = true;
3755
				}
3756
			} else {
3757
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3758
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3759
					$throw = true;
3760
				}
3761
			}
3762
3763
			if ( $throw ) {
3764
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3765
			}
3766
		}
3767
	}
3768
3769
	function intercept_plugin_error_scrape_init() {
3770
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3771
	}
3772
3773
	function intercept_plugin_error_scrape( $action, $result ) {
3774
		if ( ! $result ) {
3775
			return;
3776
		}
3777
3778
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3779
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3780
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3781
			}
3782
		}
3783
	}
3784
3785
	function add_remote_request_handlers() {
3786
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
3787
		add_action( 'wp_ajax_nopriv_jetpack_update_file', array( $this, 'remote_request_handlers' ) );
3788
	}
3789
3790
	function remote_request_handlers() {
3791
		$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...
3792
3793
		switch ( current_filter() ) {
3794
		case 'wp_ajax_nopriv_jetpack_upload_file' :
3795
			$response = $this->upload_handler();
3796
			break;
3797
3798
		case 'wp_ajax_nopriv_jetpack_update_file' :
3799
			$response = $this->upload_handler( true );
3800
			break;
3801
		default :
3802
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
3803
			break;
3804
		}
3805
3806
		if ( ! $response ) {
3807
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
3808
		}
3809
3810
		if ( is_wp_error( $response ) ) {
3811
			$status_code       = $response->get_error_data();
3812
			$error             = $response->get_error_code();
3813
			$error_description = $response->get_error_message();
3814
3815
			if ( ! is_int( $status_code ) ) {
3816
				$status_code = 400;
3817
			}
3818
3819
			status_header( $status_code );
3820
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3821
		}
3822
3823
		status_header( 200 );
3824
		if ( true === $response ) {
3825
			exit;
3826
		}
3827
3828
		die( json_encode( (object) $response ) );
3829
	}
3830
3831
	/**
3832
	 * Uploads a file gotten from the global $_FILES.
3833
	 * If `$update_media_item` is true and `post_id` is defined
3834
	 * the attachment file of the media item (gotten through of the post_id)
3835
	 * will be updated instead of add a new one.
3836
	 *
3837
	 * @param  boolean $update_media_item - update media attachment
3838
	 * @return array - An array describing the uploadind files process
3839
	 */
3840
	function upload_handler( $update_media_item = false ) {
3841
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3842
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
3843
		}
3844
3845
		$user = wp_authenticate( '', '' );
3846
		if ( ! $user || is_wp_error( $user ) ) {
3847
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
3848
		}
3849
3850
		wp_set_current_user( $user->ID );
3851
3852
		if ( ! current_user_can( 'upload_files' ) ) {
3853
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3854
		}
3855
3856
		if ( empty( $_FILES ) ) {
3857
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3858
		}
3859
3860
		foreach ( array_keys( $_FILES ) as $files_key ) {
3861
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3862
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3863
			}
3864
		}
3865
3866
		$media_keys = array_keys( $_FILES['media'] );
3867
3868
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
3869
		if ( ! $token || is_wp_error( $token ) ) {
3870
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3871
		}
3872
3873
		$uploaded_files = array();
3874
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3875
		unset( $GLOBALS['post'] );
3876
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3877
			$file = array();
3878
			foreach ( $media_keys as $media_key ) {
3879
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3880
			}
3881
3882
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3883
3884
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3885
			if ( $hmac_provided !== $hmac_file ) {
3886
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3887
				continue;
3888
			}
3889
3890
			$_FILES['.jetpack.upload.'] = $file;
3891
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3892
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3893
				$post_id = 0;
3894
			}
3895
3896
			if ( $update_media_item ) {
3897
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3898
					return new Jetpack_Error( 'invalid_input', 'Media ID must be defined.', 400 );
3899
				}
3900
3901
				$media_array = $_FILES['media'];
3902
3903
				$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...
3904
				$file_array['type'] = $media_array['type'][0];
3905
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3906
				$file_array['error'] = $media_array['error'][0];
3907
				$file_array['size'] = $media_array['size'][0];
3908
3909
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3910
3911
				if ( is_wp_error( $edited_media_item ) ) {
3912
					return $edited_media_item;
3913
				}
3914
3915
				$response = (object) array(
3916
					'id'   => (string) $post_id,
3917
					'file' => (string) $edited_media_item->post_title,
3918
					'url'  => (string) wp_get_attachment_url( $post_id ),
3919
					'type' => (string) $edited_media_item->post_mime_type,
3920
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3921
				);
3922
3923
				return (array) array( $response );
3924
			}
3925
3926
			$attachment_id = media_handle_upload(
3927
				'.jetpack.upload.',
3928
				$post_id,
3929
				array(),
3930
				array(
3931
					'action' => 'jetpack_upload_file',
3932
				)
3933
			);
3934
3935
			if ( ! $attachment_id ) {
3936
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3937
			} elseif ( is_wp_error( $attachment_id ) ) {
3938
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3939
			} else {
3940
				$attachment = get_post( $attachment_id );
3941
				$uploaded_files[$index] = (object) array(
3942
					'id'   => (string) $attachment_id,
3943
					'file' => $attachment->post_title,
3944
					'url'  => wp_get_attachment_url( $attachment_id ),
3945
					'type' => $attachment->post_mime_type,
3946
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3947
				);
3948
				// Zip files uploads are not supported unless they are done for installation purposed
3949
				// lets delete them in case something goes wrong in this whole process
3950
				if ( 'application/zip' === $attachment->post_mime_type ) {
3951
					// Schedule a cleanup for 2 hours from now in case of failed install.
3952
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3953
				}
3954
			}
3955
		}
3956
		if ( ! is_null( $global_post ) ) {
3957
			$GLOBALS['post'] = $global_post;
3958
		}
3959
3960
		return $uploaded_files;
3961
	}
3962
3963
	/**
3964
	 * Add help to the Jetpack page
3965
	 *
3966
	 * @since Jetpack (1.2.3)
3967
	 * @return false if not the Jetpack page
3968
	 */
3969
	function admin_help() {
3970
		$current_screen = get_current_screen();
3971
3972
		// Overview
3973
		$current_screen->add_help_tab(
3974
			array(
3975
				'id'		=> 'home',
3976
				'title'		=> __( 'Home', 'jetpack' ),
3977
				'content'	=>
3978
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3979
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3980
					'<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>',
3981
			)
3982
		);
3983
3984
		// Screen Content
3985
		if ( current_user_can( 'manage_options' ) ) {
3986
			$current_screen->add_help_tab(
3987
				array(
3988
					'id'		=> 'settings',
3989
					'title'		=> __( 'Settings', 'jetpack' ),
3990
					'content'	=>
3991
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3992
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3993
						'<ol>' .
3994
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3995
							'<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>' .
3996
						'</ol>' .
3997
						'<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>'
3998
				)
3999
			);
4000
		}
4001
4002
		// Help Sidebar
4003
		$current_screen->set_help_sidebar(
4004
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
4005
			'<p><a href="https://jetpack.com/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
4006
			'<p><a href="https://jetpack.com/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
4007
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
4008
		);
4009
	}
4010
4011
	function admin_menu_css() {
4012
		wp_enqueue_style( 'jetpack-icons' );
4013
	}
4014
4015
	function admin_menu_order() {
4016
		return true;
4017
	}
4018
4019 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
4020
		$jp_menu_order = array();
4021
4022
		foreach ( $menu_order as $index => $item ) {
4023
			if ( $item != 'jetpack' ) {
4024
				$jp_menu_order[] = $item;
4025
			}
4026
4027
			if ( $index == 0 ) {
4028
				$jp_menu_order[] = 'jetpack';
4029
			}
4030
		}
4031
4032
		return $jp_menu_order;
4033
	}
4034
4035
	function admin_head() {
4036 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) )
4037
			/** This action is documented in class.jetpack-admin-page.php */
4038
			do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
4039
	}
4040
4041
	function admin_banner_styles() {
4042
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
4043
4044
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
4045
			wp_register_style(
4046
				'jetpack-dops-style',
4047
				plugins_url( '_inc/build/admin.dops-style.css', JETPACK__PLUGIN_FILE ),
4048
				array(),
4049
				JETPACK__VERSION
4050
			);
4051
		}
4052
4053
		wp_enqueue_style(
4054
			'jetpack',
4055
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
4056
			array( 'jetpack-dops-style' ),
4057
			 JETPACK__VERSION . '-20121016'
4058
		);
4059
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
4060
		wp_style_add_data( 'jetpack', 'suffix', $min );
4061
	}
4062
4063
	function plugin_action_links( $actions ) {
4064
4065
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), 'Jetpack' ) );
4066
4067
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
4068
			return array_merge(
4069
				$jetpack_home,
4070
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
4071
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
4072
				$actions
4073
				);
4074
			}
4075
4076
		return array_merge( $jetpack_home, $actions );
4077
	}
4078
4079
	/**
4080
	 * This is the first banner
4081
	 * It should be visible only to user that can update the option
4082
	 * Are not connected
4083
	 *
4084
	 * @return null
4085
	 */
4086
	function admin_jetpack_manage_notice() {
4087
		$screen = get_current_screen();
4088
4089
		// Don't show the connect notice on the jetpack settings page.
4090
		if ( ! in_array( $screen->base, array( 'dashboard' ) ) || $screen->is_network || $screen->action ) {
4091
			return;
4092
		}
4093
4094
		$opt_out_url = $this->opt_out_jetpack_manage_url();
4095
		$opt_in_url  = $this->opt_in_jetpack_manage_url();
4096
		/**
4097
		 * I think it would be great to have different wordsing depending on where you are
4098
		 * for example if we show the notice on dashboard and a different one if we show it on Plugins screen
4099
		 * etc..
4100
		 */
4101
4102
		?>
4103
		<div id="message" class="updated jp-banner">
4104
				<a href="<?php echo esc_url( $opt_out_url ); ?>" class="notice-dismiss" title="<?php esc_attr_e( 'Dismiss this notice', 'jetpack' ); ?>"></a>
4105
				<div class="jp-banner__description-container">
4106
					<h2 class="jp-banner__header"><?php esc_html_e( 'Jetpack Centralized Site Management', 'jetpack' ); ?></h2>
4107
					<p class="jp-banner__description"><?php printf( __( 'Manage multiple Jetpack enabled sites from one single dashboard at wordpress.com. Allows all existing, connected Administrators to modify your site.', 'jetpack' ), 'https://jetpack.com/support/site-management' ); ?></p>
4108
					<p class="jp-banner__button-container">
4109
						<a href="<?php echo esc_url( $opt_in_url ); ?>" class="button button-primary" id="wpcom-connect"><?php _e( 'Activate Jetpack Manage', 'jetpack' ); ?></a>
4110
						<a href="https://jetpack.com/support/site-management" class="button" target="_blank" title="<?php esc_attr_e( 'Learn more about Jetpack Manage on Jetpack.com', 'jetpack' ); ?>"><?php _e( 'Learn more', 'jetpack' ); ?></a>
4111
					</p>
4112
				</div>
4113
		</div>
4114
		<?php
4115
	}
4116
4117
	/**
4118
	 * Returns the url that the user clicks to remove the notice for the big banner
4119
	 * @return string
4120
	 */
4121
	function opt_out_jetpack_manage_url() {
4122
		$referer = '&_wp_http_referer=' . add_query_arg( '_wp_http_referer', null );
4123
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-out' . $referer ), 'jetpack_manage_banner_opt_out' );
4124
	}
4125
	/**
4126
	 * Returns the url that the user clicks to opt in to Jetpack Manage
4127
	 * @return string
4128
	 */
4129
	function opt_in_jetpack_manage_url() {
4130
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-in' ), 'jetpack_manage_banner_opt_in' );
4131
	}
4132
4133
	function opt_in_jetpack_manage_notice() {
4134
		?>
4135
		<div class="wrap">
4136
			<div id="message" class="jetpack-message is-opt-in">
4137
				<?php echo sprintf( __( '<p><a href="%1$s" title="Opt in to WordPress.com Site Management" >Activate Site Management</a> to manage multiple sites from our centralized dashboard at wordpress.com/sites. <a href="%2$s" target="_blank">Learn more</a>.</p><a href="%1$s" class="jp-button">Activate Now</a>', 'jetpack' ), $this->opt_in_jetpack_manage_url(), 'https://jetpack.com/support/site-management' ); ?>
4138
			</div>
4139
		</div>
4140
		<?php
4141
4142
	}
4143
	/**
4144
	 * Determines whether to show the notice of not true = display notice
4145
	 * @return bool
4146
	 */
4147
	function can_display_jetpack_manage_notice() {
4148
		// never display the notice to users that can't do anything about it anyways
4149
		if( ! current_user_can( 'jetpack_manage_modules' ) )
4150
			return false;
4151
4152
		// don't display if we are in development more
4153
		if( Jetpack::is_development_mode() ) {
4154
			return false;
4155
		}
4156
		// don't display if the site is private
4157
		if(  ! Jetpack_Options::get_option( 'public' ) )
4158
			return false;
4159
4160
		/**
4161
		 * Should the Jetpack Remote Site Management notice be displayed.
4162
		 *
4163
		 * @since 3.3.0
4164
		 *
4165
		 * @param bool ! self::is_module_active( 'manage' ) Is the Manage module inactive.
4166
		 */
4167
		return apply_filters( 'can_display_jetpack_manage_notice', ! self::is_module_active( 'manage' ) );
4168
	}
4169
4170
	/*
4171
	 * Registration flow:
4172
	 * 1 - ::admin_page_load() action=register
4173
	 * 2 - ::try_registration()
4174
	 * 3 - ::register()
4175
	 *     - Creates jetpack_register option containing two secrets and a timestamp
4176
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4177
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4178
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
4179
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4180
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4181
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4182
	 *       jetpack_id, jetpack_secret, jetpack_public
4183
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4184
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4185
	 * 5 - user logs in with WP.com account
4186
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4187
	 *		- Jetpack_Client_Server::authorize()
4188
	 *		- Jetpack_Client_Server::get_token()
4189
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
4190
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4191
	 *			- which responds with access_token, token_type, scope
4192
	 *		- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
4193
	 *		- Jetpack::activate_default_modules()
4194
	 *     		- Deactivates deprecated plugins
4195
	 *     		- Activates all default modules
4196
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4197
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4198
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4199
	 *     Done!
4200
	 */
4201
4202
	/**
4203
	 * Handles the page load events for the Jetpack admin page
4204
	 */
4205
	function admin_page_load() {
4206
		$error = false;
4207
4208
		// Make sure we have the right body class to hook stylings for subpages off of.
4209
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
4210
4211
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
4212
			// Should only be used in intermediate redirects to preserve state across redirects
4213
			Jetpack::restate();
4214
		}
4215
4216
		if ( isset( $_GET['connect_url_redirect'] ) ) {
4217
			// User clicked in the iframe to link their accounts
4218
			if ( ! Jetpack::is_user_connected() ) {
4219
				$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4220
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4221
4222
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4223
				$connect_url = $this->build_connect_url( true, $redirect, $from );
4224
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4225
4226
				if ( isset( $_GET['notes_iframe'] ) )
4227
					$connect_url .= '&notes_iframe';
4228
				wp_redirect( $connect_url );
4229
				exit;
4230
			} else {
4231
				if ( ! isset( $_GET['calypso_env'] ) ) {
4232
					Jetpack::state( 'message', 'already_authorized' );
4233
					wp_safe_redirect( Jetpack::admin_url() );
4234
					exit;
4235
				} else {
4236
					$connect_url = $this->build_connect_url( true, false, 'iframe' );
4237
					$connect_url .= '&already_authorized=true';
4238
					wp_redirect( $connect_url );
4239
					exit;
4240
				}
4241
			}
4242
		}
4243
4244
4245
		if ( isset( $_GET['action'] ) ) {
4246
			switch ( $_GET['action'] ) {
4247
			case 'authorize':
4248
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
4249
					Jetpack::state( 'message', 'already_authorized' );
4250
					wp_safe_redirect( Jetpack::admin_url() );
4251
					exit;
4252
				}
4253
				Jetpack::log( 'authorize' );
4254
				$client_server = new Jetpack_Client_Server;
4255
				$client_server->client_authorize();
4256
				exit;
4257
			case 'register' :
4258
				if ( ! current_user_can( 'jetpack_connect' ) ) {
4259
					$error = 'cheatin';
4260
					break;
4261
				}
4262
				check_admin_referer( 'jetpack-register' );
4263
				Jetpack::log( 'register' );
4264
				Jetpack::maybe_set_version_option();
4265
				$registered = Jetpack::try_registration();
4266
				if ( is_wp_error( $registered ) ) {
4267
					$error = $registered->get_error_code();
4268
					Jetpack::state( 'error', $error );
4269
					Jetpack::state( 'error', $registered->get_error_message() );
4270
					JetpackTracking::record_user_event( 'jpc_register_fail', array(
4271
						'error_code' => $error,
4272
						'error_message' => $registered->get_error_message()
4273
					) );
4274
					break;
4275
				}
4276
4277
				$from = isset( $_GET['from'] ) ? $_GET['from'] : false;
4278
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4279
4280
				JetpackTracking::record_user_event( 'jpc_register_success', array(
4281
					'from' => $from
4282
				) );
4283
4284
				$url = $this->build_connect_url( true, $redirect, $from );
4285
4286
				if ( ! empty( $_GET['onboarding'] ) ) {
4287
					$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4288
				}
4289
4290
				if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4291
					$url = add_query_arg( 'auth_approved', 'true', $url );
4292
				}
4293
4294
				wp_redirect( $url );
4295
				exit;
4296
			case 'activate' :
4297
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4298
					$error = 'cheatin';
4299
					break;
4300
				}
4301
4302
				$module = stripslashes( $_GET['module'] );
4303
				check_admin_referer( "jetpack_activate-$module" );
4304
				Jetpack::log( 'activate', $module );
4305
				if ( ! Jetpack::activate_module( $module ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \Jetpack::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...
4306
					Jetpack::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4307
				}
4308
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4309
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4310
				exit;
4311
			case 'activate_default_modules' :
4312
				check_admin_referer( 'activate_default_modules' );
4313
				Jetpack::log( 'activate_default_modules' );
4314
				Jetpack::restate();
4315
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4316
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4317
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4318
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
4319
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4320
				exit;
4321
			case 'disconnect' :
4322
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4323
					$error = 'cheatin';
4324
					break;
4325
				}
4326
4327
				check_admin_referer( 'jetpack-disconnect' );
4328
				Jetpack::log( 'disconnect' );
4329
				Jetpack::disconnect();
4330
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
4331
				exit;
4332
			case 'reconnect' :
4333
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4334
					$error = 'cheatin';
4335
					break;
4336
				}
4337
4338
				check_admin_referer( 'jetpack-reconnect' );
4339
				Jetpack::log( 'reconnect' );
4340
				$this->disconnect();
4341
				wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4342
				exit;
4343 View Code Duplication
			case 'deactivate' :
4344
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4345
					$error = 'cheatin';
4346
					break;
4347
				}
4348
4349
				$modules = stripslashes( $_GET['module'] );
4350
				check_admin_referer( "jetpack_deactivate-$modules" );
4351
				foreach ( explode( ',', $modules ) as $module ) {
4352
					Jetpack::log( 'deactivate', $module );
4353
					Jetpack::deactivate_module( $module );
4354
					Jetpack::state( 'message', 'module_deactivated' );
4355
				}
4356
				Jetpack::state( 'module', $modules );
4357
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4358
				exit;
4359
			case 'unlink' :
4360
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4361
				check_admin_referer( 'jetpack-unlink' );
4362
				Jetpack::log( 'unlink' );
4363
				$this->unlink_user();
4364
				Jetpack::state( 'message', 'unlinked' );
4365
				if ( 'sub-unlink' == $redirect ) {
4366
					wp_safe_redirect( admin_url() );
4367
				} else {
4368
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
4369
				}
4370
				exit;
4371
			case 'onboard' :
4372
				if ( ! current_user_can( 'manage_options' ) ) {
4373
					wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4374
				} else {
4375
					Jetpack::create_onboarding_token();
4376
					$url = $this->build_connect_url( true );
4377
4378
					if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4379
						$url = add_query_arg( 'onboarding', $token, $url );
4380
					}
4381
4382
					$calypso_env = ! empty( $_GET[ 'calypso_env' ] ) ? $_GET[ 'calypso_env' ] : false;
4383
					if ( $calypso_env ) {
4384
						$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4385
					}
4386
4387
					wp_redirect( $url );
4388
					exit;
4389
				}
4390
				exit;
4391
			default:
4392
				/**
4393
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4394
				 *
4395
				 * @since 2.6.0
4396
				 *
4397
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4398
				 */
4399
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4400
			}
4401
		}
4402
4403
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
4404
			self::activate_new_modules( true );
4405
		}
4406
4407
		$message_code = Jetpack::state( 'message' );
4408
		if ( Jetpack::state( 'optin-manage' ) ) {
4409
			$activated_manage = $message_code;
4410
			$message_code = 'jetpack-manage';
4411
		}
4412
4413
		switch ( $message_code ) {
4414
		case 'jetpack-manage':
4415
			$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' ), 'https://wordpress.com/sites' ) . '</strong>';
4416
			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...
4417
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
4418
			}
4419
			break;
4420
4421
		}
4422
4423
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
4424
4425
		if ( ! empty( $deactivated_plugins ) ) {
4426
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4427
			$deactivated_titles  = array();
4428
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4429
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
4430
					continue;
4431
				}
4432
4433
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
4434
			}
4435
4436
			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...
4437
				if ( $this->message ) {
4438
					$this->message .= "<br /><br />\n";
4439
				}
4440
4441
				$this->message .= wp_sprintf(
4442
					_n(
4443
						'Jetpack contains the most recent version of the old %l plugin.',
4444
						'Jetpack contains the most recent versions of the old %l plugins.',
4445
						count( $deactivated_titles ),
4446
						'jetpack'
4447
					),
4448
					$deactivated_titles
4449
				);
4450
4451
				$this->message .= "<br />\n";
4452
4453
				$this->message .= _n(
4454
					'The old version has been deactivated and can be removed from your site.',
4455
					'The old versions have been deactivated and can be removed from your site.',
4456
					count( $deactivated_titles ),
4457
					'jetpack'
4458
				);
4459
			}
4460
		}
4461
4462
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
4463
4464
		if ( $this->message || $this->error || $this->privacy_checks || $this->can_display_jetpack_manage_notice() ) {
4465
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4466
		}
4467
4468 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
4469
			/**
4470
			 * Fires when a module configuration page is loaded.
4471
			 * The dynamic part of the hook is the configure parameter from the URL.
4472
			 *
4473
			 * @since 1.1.0
4474
			 */
4475
			do_action( 'jetpack_module_configuration_load_' . $_GET['configure'] );
4476
		}
4477
4478
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4479
	}
4480
4481
	function admin_notices() {
4482
4483
		if ( $this->error ) {
4484
?>
4485
<div id="message" class="jetpack-message jetpack-err">
4486
	<div class="squeezer">
4487
		<h2><?php echo wp_kses( $this->error, array( 'a' => array( 'href' => array() ), 'small' => true, 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
4488
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
4489
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4490
<?php	endif; ?>
4491
	</div>
4492
</div>
4493
<?php
4494
		}
4495
4496
		if ( $this->message ) {
4497
?>
4498
<div id="message" class="jetpack-message">
4499
	<div class="squeezer">
4500
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
4501
	</div>
4502
</div>
4503
<?php
4504
		}
4505
4506
		if ( $this->privacy_checks ) :
4507
			$module_names = $module_slugs = array();
4508
4509
			$privacy_checks = explode( ',', $this->privacy_checks );
4510
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4511
			foreach ( $privacy_checks as $module_slug ) {
4512
				$module = Jetpack::get_module( $module_slug );
4513
				if ( ! $module ) {
4514
					continue;
4515
				}
4516
4517
				$module_slugs[] = $module_slug;
4518
				$module_names[] = "<strong>{$module['name']}</strong>";
4519
			}
4520
4521
			$module_slugs = join( ',', $module_slugs );
4522
?>
4523
<div id="message" class="jetpack-message jetpack-err">
4524
	<div class="squeezer">
4525
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4526
		<p><?php
4527
			echo wp_kses(
4528
				wptexturize(
4529
					wp_sprintf(
4530
						_nx(
4531
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4532
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4533
							count( $privacy_checks ),
4534
							'%l = list of Jetpack module/feature names',
4535
							'jetpack'
4536
						),
4537
						$module_names
4538
					)
4539
				),
4540
				array( 'strong' => true )
4541
			);
4542
4543
			echo "\n<br />\n";
4544
4545
			echo wp_kses(
4546
				sprintf(
4547
					_nx(
4548
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4549
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4550
						count( $privacy_checks ),
4551
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4552
						'jetpack'
4553
					),
4554
					wp_nonce_url(
4555
						Jetpack::admin_url(
4556
							array(
4557
								'page'   => 'jetpack',
4558
								'action' => 'deactivate',
4559
								'module' => urlencode( $module_slugs ),
4560
							)
4561
						),
4562
						"jetpack_deactivate-$module_slugs"
4563
					),
4564
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4565
				),
4566
				array( 'a' => array( 'href' => true, 'title' => true ) )
4567
			);
4568
		?></p>
4569
	</div>
4570
</div>
4571
<?php endif;
4572
	// only display the notice if the other stuff is not there
4573
	if( $this->can_display_jetpack_manage_notice() && !  $this->error && ! $this->message && ! $this->privacy_checks ) {
4574
		if( isset( $_GET['page'] ) && 'jetpack' != $_GET['page'] )
4575
			$this->opt_in_jetpack_manage_notice();
4576
		}
4577
	}
4578
4579
	/**
4580
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4581
	 */
4582
	function stat( $group, $detail ) {
4583
		if ( ! isset( $this->stats[ $group ] ) )
4584
			$this->stats[ $group ] = array();
4585
		$this->stats[ $group ][] = $detail;
4586
	}
4587
4588
	/**
4589
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4590
	 */
4591
	function do_stats( $method = '' ) {
4592
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4593
			foreach ( $this->stats as $group => $stats ) {
4594
				if ( is_array( $stats ) && count( $stats ) ) {
4595
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4596
					if ( 'server_side' === $method ) {
4597
						self::do_server_side_stat( $args );
4598
					} else {
4599
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4600
					}
4601
				}
4602
				unset( $this->stats[ $group ] );
4603
			}
4604
		}
4605
	}
4606
4607
	/**
4608
	 * Runs stats code for a one-off, server-side.
4609
	 *
4610
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4611
	 *
4612
	 * @return bool If it worked.
4613
	 */
4614
	static function do_server_side_stat( $args ) {
4615
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4616
		if ( is_wp_error( $response ) )
4617
			return false;
4618
4619
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
4620
			return false;
4621
4622
		return true;
4623
	}
4624
4625
	/**
4626
	 * Builds the stats url.
4627
	 *
4628
	 * @param $args array|string The arguments to append to the URL.
4629
	 *
4630
	 * @return string The URL to be pinged.
4631
	 */
4632
	static function build_stats_url( $args ) {
4633
		$defaults = array(
4634
			'v'    => 'wpcom2',
4635
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4636
		);
4637
		$args     = wp_parse_args( $args, $defaults );
4638
		/**
4639
		 * Filter the URL used as the Stats tracking pixel.
4640
		 *
4641
		 * @since 2.3.2
4642
		 *
4643
		 * @param string $url Base URL used as the Stats tracking pixel.
4644
		 */
4645
		$base_url = apply_filters(
4646
			'jetpack_stats_base_url',
4647
			'https://pixel.wp.com/g.gif'
4648
		);
4649
		$url      = add_query_arg( $args, $base_url );
4650
		return $url;
4651
	}
4652
4653
	static function translate_current_user_to_role() {
4654
		foreach ( self::$capability_translations as $role => $cap ) {
4655
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
4656
				return $role;
4657
			}
4658
		}
4659
4660
		return false;
4661
	}
4662
4663
	static function translate_user_to_role( $user ) {
4664
		foreach ( self::$capability_translations as $role => $cap ) {
4665
			if ( user_can( $user, $role ) || user_can( $user, $cap ) ) {
4666
				return $role;
4667
			}
4668
		}
4669
4670
		return false;
4671
    }
4672
4673
	static function translate_role_to_cap( $role ) {
4674
		if ( ! isset( self::$capability_translations[$role] ) ) {
4675
			return false;
4676
		}
4677
4678
		return self::$capability_translations[$role];
4679
	}
4680
4681
	static function sign_role( $role, $user_id = null ) {
4682
		if ( empty( $user_id ) ) {
4683
			$user_id = (int) get_current_user_id();
4684
		}
4685
4686
		if ( ! $user_id  ) {
4687
			return false;
4688
		}
4689
4690
		$token = Jetpack_Data::get_access_token();
4691
		if ( ! $token || is_wp_error( $token ) ) {
4692
			return false;
4693
		}
4694
4695
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4696
	}
4697
4698
4699
	/**
4700
	 * Builds a URL to the Jetpack connection auth page
4701
	 *
4702
	 * @since 3.9.5
4703
	 *
4704
	 * @param bool $raw If true, URL will not be escaped.
4705
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4706
	 *                              If string, will be a custom redirect.
4707
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4708
	 * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4709
	 *
4710
	 * @return string Connect URL
4711
	 */
4712
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4713
		$site_id = Jetpack_Options::get_option( 'id' );
4714
		$token = Jetpack_Options::get_option( 'blog_token' );
4715
4716
		if ( $register || ! $token || ! $site_id ) {
4717
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4718
4719
			if ( ! empty( $redirect ) ) {
4720
				$url = add_query_arg(
4721
					'redirect',
4722
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4723
					$url
4724
				);
4725
			}
4726
4727
			if( is_network_admin() ) {
4728
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4729
			}
4730
		} else {
4731
4732
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4733
			// because otherwise this logic can get us in to a loop.
4734
			$last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) );
4735
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4736
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4737
4738
				$response = Jetpack_Client::wpcom_json_api_request_as_blog(
4739
					sprintf( '/sites/%d', $site_id ) .'?force=wpcom',
4740
					'1.1'
4741
				);
4742
4743
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4744
					// Generating a register URL instead to refresh the existing token
4745
					return $this->build_connect_url( $raw, $redirect, $from, true );
4746
				}
4747
			}
4748
4749
			if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && include_once JETPACK__GLOTPRESS_LOCALES_PATH ) {
4750
				$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4751
			}
4752
4753
			$role = self::translate_current_user_to_role();
4754
			$signed_role = self::sign_role( $role );
4755
4756
			$user = wp_get_current_user();
4757
4758
			$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4759
			$redirect = $redirect
4760
				? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4761
				: $jetpack_admin_page;
4762
4763
			if( isset( $_REQUEST['is_multisite'] ) ) {
4764
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4765
			}
4766
4767
			$secrets = Jetpack::generate_secrets( 'authorize', false, 2 * HOUR_IN_SECONDS );
4768
4769
			$site_icon = ( function_exists( 'has_site_icon') && has_site_icon() )
4770
				? get_site_icon_url()
4771
				: false;
4772
4773
			/**
4774
			 * Filter the type of authorization.
4775
			 * 'calypso' completes authorization on wordpress.com/jetpack/connect
4776
			 * while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
4777
			 *
4778
			 * @since 4.3.3
4779
			 *
4780
			 * @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
4781
			 */
4782
			$auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );
4783
4784
			$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
4785
4786
			$args = urlencode_deep(
4787
				array(
4788
					'response_type' => 'code',
4789
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4790
					'redirect_uri'  => add_query_arg(
4791
						array(
4792
							'action'   => 'authorize',
4793
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4794
							'redirect' => urlencode( $redirect ),
4795
						),
4796
						esc_url( admin_url( 'admin.php?page=jetpack' ) )
4797
					),
4798
					'state'         => $user->ID,
4799
					'scope'         => $signed_role,
4800
					'user_email'    => $user->user_email,
4801
					'user_login'    => $user->user_login,
4802
					'is_active'     => Jetpack::is_active(),
4803
					'jp_version'    => JETPACK__VERSION,
4804
					'auth_type'     => $auth_type,
4805
					'secret'        => $secrets['secret_1'],
4806
					'locale'        => ( isset( $gp_locale ) && isset( $gp_locale->slug ) ) ? $gp_locale->slug : '',
4807
					'blogname'      => get_option( 'blogname' ),
4808
					'site_url'      => site_url(),
4809
					'home_url'      => home_url(),
4810
					'site_icon'     => $site_icon,
4811
					'site_lang'     => get_locale(),
4812
					'_ui'           => $tracks_identity['_ui'],
4813
					'_ut'           => $tracks_identity['_ut'],
4814
				)
4815
			);
4816
4817
			self::apply_activation_source_to_args( $args );
4818
4819
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4820
		}
4821
4822
		if ( $from ) {
4823
			$url = add_query_arg( 'from', $from, $url );
4824
		}
4825
4826
		// Ensure that class to get the affiliate code is loaded
4827
		if ( ! class_exists( 'Jetpack_Affiliate' ) ) {
4828
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-affiliate.php';
4829
		}
4830
		// Get affiliate code and add it to the URL
4831
		$url = Jetpack_Affiliate::init()->add_code_as_query_arg( $url );
4832
4833
		if ( isset( $_GET['calypso_env'] ) ) {
4834
			$url = add_query_arg( 'calypso_env', sanitize_key( $_GET['calypso_env'] ), $url );
4835
		}
4836
4837
		return $raw ? $url : esc_url( $url );
4838
	}
4839
4840
	public static function apply_activation_source_to_args( &$args ) {
4841
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
4842
4843
		if ( $activation_source_name ) {
4844
			$args['_as'] = urlencode( $activation_source_name );
4845
		}
4846
4847
		if ( $activation_source_keyword ) {
4848
			$args['_ak'] = urlencode( $activation_source_keyword );
4849
		}
4850
	}
4851
4852
	function build_reconnect_url( $raw = false ) {
4853
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4854
		return $raw ? $url : esc_url( $url );
4855
	}
4856
4857
	public static function admin_url( $args = null ) {
4858
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4859
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4860
		return $url;
4861
	}
4862
4863
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4864
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4865
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4866
	}
4867
4868
	function dismiss_jetpack_notice() {
4869
4870
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4871
			return;
4872
		}
4873
4874
		switch( $_GET['jetpack-notice'] ) {
4875
			case 'dismiss':
4876
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4877
4878
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4879
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4880
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4881
				}
4882
				break;
4883 View Code Duplication
			case 'jetpack-manage-opt-out':
4884
4885
				if ( check_admin_referer( 'jetpack_manage_banner_opt_out' ) ) {
4886
					// Don't show the banner again
4887
4888
					Jetpack_Options::update_option( 'dismissed_manage_banner', true );
4889
					// redirect back to the page that had the notice
4890
					if ( wp_get_referer() ) {
4891
						wp_safe_redirect( wp_get_referer() );
4892
					} else {
4893
						// Take me to Jetpack
4894
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4895
					}
4896
				}
4897
				break;
4898 View Code Duplication
			case 'jetpack-protect-multisite-opt-out':
4899
4900
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4901
					// Don't show the banner again
4902
4903
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4904
					// redirect back to the page that had the notice
4905
					if ( wp_get_referer() ) {
4906
						wp_safe_redirect( wp_get_referer() );
4907
					} else {
4908
						// Take me to Jetpack
4909
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4910
					}
4911
				}
4912
				break;
4913
			case 'jetpack-manage-opt-in':
4914
				if ( check_admin_referer( 'jetpack_manage_banner_opt_in' ) ) {
4915
					// This makes sure that we are redirect to jetpack home so that we can see the Success Message.
4916
4917
					$redirection_url = Jetpack::admin_url();
4918
					remove_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4919
4920
					// Don't redirect form the Jetpack Setting Page
4921
					$referer_parsed = parse_url ( wp_get_referer() );
4922
					// check that we do have a wp_get_referer and the query paramater is set orderwise go to the Jetpack Home
4923
					if ( isset( $referer_parsed['query'] ) && false !== strpos( $referer_parsed['query'], 'page=jetpack_modules' ) ) {
4924
						// Take the user to Jetpack home except when on the setting page
4925
						$redirection_url = wp_get_referer();
4926
						add_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4927
					}
4928
					// Also update the JSON API FULL MANAGEMENT Option
4929
					Jetpack::activate_module( 'manage', false, false );
4930
4931
					// Special Message when option in.
4932
					Jetpack::state( 'optin-manage', 'true' );
4933
					// Activate the Module if not activated already
4934
4935
					// Redirect properly
4936
					wp_safe_redirect( $redirection_url );
4937
4938
				}
4939
				break;
4940
		}
4941
	}
4942
4943
	public static function admin_screen_configure_module( $module_id ) {
4944
4945
		// User that doesn't have 'jetpack_configure_modules' will never end up here since Jetpack Landing Page woun't let them.
4946
		if ( ! in_array( $module_id, Jetpack::get_active_modules() ) && current_user_can( 'manage_options' ) ) {
4947
			if ( has_action( 'display_activate_module_setting_' . $module_id ) ) {
4948
				/**
4949
				 * Fires to diplay a custom module activation screen.
4950
				 *
4951
				 * To add a module actionation screen use Jetpack::module_configuration_activation_screen method.
4952
				 * Example: Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
4953
				 *
4954
				 * @module manage
4955
				 *
4956
				 * @since 3.8.0
4957
				 *
4958
				 * @param int $module_id Module ID.
4959
				 */
4960
				do_action( 'display_activate_module_setting_' . $module_id );
4961
			} else {
4962
				self::display_activate_module_link( $module_id );
4963
			}
4964
4965
			return false;
4966
		} ?>
4967
4968
		<div id="jp-settings-screen" style="position: relative">
4969
			<h3>
4970
			<?php
4971
				$module = Jetpack::get_module( $module_id );
4972
				printf( __( 'Configure %s', 'jetpack' ), $module['name'] );
4973
			?>
4974
			</h3>
4975
			<?php
4976
				/**
4977
				 * Fires within the displayed message when a feature configuation is updated.
4978
				 *
4979
				 * @since 3.4.0
4980
				 *
4981
				 * @param int $module_id Module ID.
4982
				 */
4983
				do_action( 'jetpack_notices_update_settings', $module_id );
4984
				/**
4985
				 * Fires when a feature configuation screen is loaded.
4986
				 * The dynamic part of the hook, $module_id, is the module ID.
4987
				 *
4988
				 * @since 1.1.0
4989
				 */
4990
				do_action( 'jetpack_module_configuration_screen_' . $module_id );
4991
			?>
4992
		</div><?php
4993
	}
4994
4995
	/**
4996
	 * Display link to activate the module to see the settings screen.
4997
	 * @param  string $module_id
4998
	 * @return null
4999
	 */
5000
	public static function display_activate_module_link( $module_id ) {
5001
5002
		$info =  Jetpack::get_module( $module_id );
5003
		$extra = '';
5004
		$activate_url = wp_nonce_url(
5005
				Jetpack::admin_url(
5006
					array(
5007
						'page'   => 'jetpack',
5008
						'action' => 'activate',
5009
						'module' => $module_id,
5010
					)
5011
				),
5012
				"jetpack_activate-$module_id"
5013
			);
5014
5015
		?>
5016
5017
		<div class="wrap configure-module">
5018
			<div id="jp-settings-screen">
5019
				<?php
5020
				if ( $module_id == 'json-api' ) {
5021
5022
					$info['name'] = esc_html__( 'Activate Site Management and JSON API', 'jetpack' );
5023
5024
					$activate_url = Jetpack::init()->opt_in_jetpack_manage_url();
5025
5026
					$info['description'] = sprintf( __( 'Manage your multiple Jetpack sites from our centralized dashboard at wordpress.com/sites. <a href="%s" target="_blank">Learn more</a>.', 'jetpack' ), 'https://jetpack.com/support/site-management' );
5027
5028
					// $extra = __( 'To use Site Management, you need to first activate JSON API to allow remote management of your site. ', 'jetpack' );
5029
				} ?>
5030
5031
				<h3><?php echo esc_html( $info['name'] ); ?></h3>
5032
				<div class="narrow">
5033
					<p><?php echo  $info['description']; ?></p>
5034
					<?php if( $extra ) { ?>
5035
					<p><?php echo esc_html( $extra ); ?></p>
5036
					<?php } ?>
5037
					<p>
5038
						<?php
5039
						if( wp_get_referer() ) {
5040
							printf( __( '<a class="button-primary" href="%s">Activate Now</a> or <a href="%s" >return to previous page</a>.', 'jetpack' ) , $activate_url, wp_get_referer() );
5041
						} else {
5042
							printf( __( '<a class="button-primary" href="%s">Activate Now</a>', 'jetpack' ) , $activate_url  );
5043
						} ?>
5044
					</p>
5045
				</div>
5046
5047
			</div>
5048
		</div>
5049
5050
		<?php
5051
	}
5052
5053
	public static function sort_modules( $a, $b ) {
5054
		if ( $a['sort'] == $b['sort'] )
5055
			return 0;
5056
5057
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
5058
	}
5059
5060
	function ajax_recheck_ssl() {
5061
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
5062
		$result = Jetpack::permit_ssl( true );
5063
		wp_send_json( array(
5064
			'enabled' => $result,
5065
			'message' => get_transient( 'jetpack_https_test_message' )
5066
		) );
5067
	}
5068
5069
/* Client API */
5070
5071
	/**
5072
	 * Returns the requested Jetpack API URL
5073
	 *
5074
	 * @return string
5075
	 */
5076
	public static function api_url( $relative_url ) {
5077
		return trailingslashit( JETPACK__API_BASE . $relative_url  ) . JETPACK__API_VERSION . '/';
5078
	}
5079
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
		if ( 0 !== strpos( $url, 'https://' ) ) {
5085
			return $url;
5086
		}
5087
5088
		switch ( JETPACK_CLIENT__HTTPS ) {
5089
			case 'ALWAYS' :
5090
				return $url;
5091
			case 'NEVER' :
5092
				return set_url_scheme( $url, 'http' );
5093
			// default : case 'AUTO' :
5094
		}
5095
5096
		// we now return the unmodified SSL URL by default, as a security precaution
5097
		return $url;
5098
	}
5099
5100
	/**
5101
	 * Create a random secret for validating onboarding payload
5102
	 *
5103
	 * @return string Secret token
5104
	 */
5105
	public static function create_onboarding_token() {
5106
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
5107
			$token = wp_generate_password( 32, false );
5108
			Jetpack_Options::update_option( 'onboarding', $token );
5109
		}
5110
5111
		return $token;
5112
	}
5113
5114
	/**
5115
	 * Remove the onboarding token
5116
	 *
5117
	 * @return bool True on success, false on failure
5118
	 */
5119
	public static function invalidate_onboarding_token() {
5120
		return Jetpack_Options::delete_option( 'onboarding' );
5121
	}
5122
5123
	/**
5124
	 * Validate an onboarding token for a specific action
5125
	 *
5126
	 * @return boolean True if token/action pair is accepted, false if not
5127
	 */
5128
	public static function validate_onboarding_token_action( $token, $action ) {
5129
		// Compare tokens, bail if tokens do not match
5130
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5131
			return false;
5132
		}
5133
5134
		// List of valid actions we can take
5135
		$valid_actions = array(
5136
			'/jetpack/v4/settings',
5137
		);
5138
5139
		// Whitelist the action
5140
		if ( ! in_array( $action, $valid_actions ) ) {
5141
			return false;
5142
		}
5143
5144
		return true;
5145
	}
5146
5147
	/**
5148
	 * Checks to see if the URL is using SSL to connect with Jetpack
5149
	 *
5150
	 * @since 2.3.3
5151
	 * @return boolean
5152
	 */
5153
	public static function permit_ssl( $force_recheck = false ) {
5154
		// Do some fancy tests to see if ssl is being supported
5155
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5156
			$message = '';
5157
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5158
				$ssl = 0;
5159
			} else {
5160
				switch ( JETPACK_CLIENT__HTTPS ) {
5161
					case 'NEVER':
5162
						$ssl = 0;
5163
						$message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' );
5164
						break;
5165
					case 'ALWAYS':
5166
					case 'AUTO':
5167
					default:
5168
						$ssl = 1;
5169
						break;
5170
				}
5171
5172
				// If it's not 'NEVER', test to see
5173
				if ( $ssl ) {
5174
					if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5175
						$ssl = 0;
5176
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
5177
					} else {
5178
						$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5179
						if ( is_wp_error( $response ) ) {
5180
							$ssl = 0;
5181
							$message = __( 'WordPress reports no SSL support', 'jetpack' );
5182
						} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5183
							$ssl = 0;
5184
							$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5185
						}
5186
					}
5187
				}
5188
			}
5189
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5190
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5191
		}
5192
5193
		return (bool) $ssl;
5194
	}
5195
5196
	/*
5197
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working.
5198
	 */
5199
	public function alert_auto_ssl_fail() {
5200
		if ( ! current_user_can( 'manage_options' ) )
5201
			return;
5202
5203
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5204
		?>
5205
5206
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5207
			<div class="jp-banner__content">
5208
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5209
				<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>
5210
				<p>
5211
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5212
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5213
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5214
				</p>
5215
				<p>
5216
					<?php printf( __( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
5217
							esc_url( Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) ),
5218
							esc_url( 'https://jetpack.com/support/getting-started-with-jetpack/troubleshooting-tips/' ) ); ?>
5219
				</p>
5220
			</div>
5221
		</div>
5222
		<style>
5223
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5224
		</style>
5225
		<script type="text/javascript">
5226
			jQuery( document ).ready( function( $ ) {
5227
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5228
					var $this = $( this );
5229
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5230
					$( '#jetpack-recheck-ssl-output' ).html( '' );
5231
					e.preventDefault();
5232
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5233
					$.post( ajaxurl, data )
5234
					  .done( function( response ) {
5235
					  	if ( response.enabled ) {
5236
					  		$( '#jetpack-ssl-warning' ).hide();
5237
					  	} else {
5238
					  		this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5239
					  		$( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5240
					  	}
5241
					  }.bind( $this ) );
5242
				} );
5243
			} );
5244
		</script>
5245
5246
		<?php
5247
	}
5248
5249
	/**
5250
	 * Returns the Jetpack XML-RPC API
5251
	 *
5252
	 * @return string
5253
	 */
5254
	public static function xmlrpc_api_url() {
5255
		$base = preg_replace( '#(https?://[^?/]+)(/?.*)?$#', '\\1', JETPACK__API_BASE );
5256
		return untrailingslashit( $base ) . '/xmlrpc.php';
5257
	}
5258
5259
	/**
5260
	 * Creates two secret tokens and the end of life timestamp for them.
5261
	 *
5262
	 * Note these tokens are unique per call, NOT static per site for connecting.
5263
	 *
5264
	 * @since 2.6
5265
	 * @return array
5266
	 */
5267
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5268
		if ( ! $user_id ) {
5269
			$user_id = get_current_user_id();
5270
		}
5271
5272
		$secret_name  = 'jetpack_' . $action . '_' . $user_id;
5273
		$secrets      = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5274
5275
		if (
5276
			isset( $secrets[ $secret_name ] ) &&
5277
			$secrets[ $secret_name ]['exp'] > time()
5278
		) {
5279
			return $secrets[ $secret_name ];
5280
		}
5281
5282
		$secret_value = array(
5283
			'secret_1'  => wp_generate_password( 32, false ),
5284
			'secret_2'  => wp_generate_password( 32, false ),
5285
			'exp'       => time() + $exp,
5286
		);
5287
5288
		$secrets[ $secret_name ] = $secret_value;
5289
5290
		Jetpack_Options::update_raw_option( 'jetpack_secrets', $secrets );
5291
		return $secrets[ $secret_name ];
5292
	}
5293
5294
	public static function get_secrets( $action, $user_id ) {
5295
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
5296
		$secrets = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5297
5298
		if ( ! isset( $secrets[ $secret_name ] ) ) {
5299
			return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
5300
		}
5301
5302
		if ( $secrets[ $secret_name ]['exp'] < time() ) {
5303
			self::delete_secrets( $action, $user_id );
5304
			return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
5305
		}
5306
5307
		return $secrets[ $secret_name ];
5308
	}
5309
5310
	public static function delete_secrets( $action, $user_id ) {
5311
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
5312
		$secrets = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5313
		if ( isset( $secrets[ $secret_name ] ) ) {
5314
			unset( $secrets[ $secret_name ] );
5315
			Jetpack_Options::update_raw_option( 'jetpack_secrets', $secrets );
5316
		}
5317
	}
5318
5319
	/**
5320
	 * Builds the timeout limit for queries talking with the wpcom servers.
5321
	 *
5322
	 * Based on local php max_execution_time in php.ini
5323
	 *
5324
	 * @since 2.6
5325
	 * @return int
5326
	 * @deprecated
5327
	 **/
5328
	public function get_remote_query_timeout_limit() {
5329
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
5330
		return Jetpack::get_max_execution_time();
5331
	}
5332
5333
	/**
5334
	 * Builds the timeout limit for queries talking with the wpcom servers.
5335
	 *
5336
	 * Based on local php max_execution_time in php.ini
5337
	 *
5338
	 * @since 5.4
5339
	 * @return int
5340
	 **/
5341
	public static function get_max_execution_time() {
5342
		$timeout = (int) ini_get( 'max_execution_time' );
5343
5344
		// Ensure exec time set in php.ini
5345
		if ( ! $timeout ) {
5346
			$timeout = 30;
5347
		}
5348
		return $timeout;
5349
	}
5350
5351
	/**
5352
	 * Sets a minimum request timeout, and returns the current timeout
5353
	 *
5354
	 * @since 5.4
5355
	 **/
5356
	public static function set_min_time_limit( $min_timeout ) {
5357
		$timeout = self::get_max_execution_time();
5358
		if ( $timeout < $min_timeout ) {
5359
			$timeout = $min_timeout;
5360
			set_time_limit( $timeout );
5361
		}
5362
		return $timeout;
5363
	}
5364
5365
5366
	/**
5367
	 * Takes the response from the Jetpack register new site endpoint and
5368
	 * verifies it worked properly.
5369
	 *
5370
	 * @since 2.6
5371
	 * @return string|Jetpack_Error A JSON object on success or Jetpack_Error on failures
5372
	 **/
5373
	public function validate_remote_register_response( $response ) {
5374
	  if ( is_wp_error( $response ) ) {
5375
			return new Jetpack_Error( 'register_http_request_failed', $response->get_error_message() );
5376
		}
5377
5378
		$code   = wp_remote_retrieve_response_code( $response );
5379
		$entity = wp_remote_retrieve_body( $response );
5380
		if ( $entity )
5381
			$registration_response = json_decode( $entity );
5382
		else
5383
			$registration_response = false;
5384
5385
		$code_type = intval( $code / 100 );
5386
		if ( 5 == $code_type ) {
5387
			return new Jetpack_Error( 'wpcom_5??', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5388
		} elseif ( 408 == $code ) {
5389
			return new Jetpack_Error( 'wpcom_408', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5390
		} elseif ( ! empty( $registration_response->error ) ) {
5391
			if ( 'xml_rpc-32700' == $registration_response->error && ! function_exists( 'xml_parser_create' ) ) {
5392
				$error_description = __( "PHP's XML extension is not available. Jetpack requires the XML extension to communicate with WordPress.com. Please contact your hosting provider to enable PHP's XML extension.", 'jetpack' );
5393
			} else {
5394
				$error_description = isset( $registration_response->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $registration_response->error_description ) : '';
5395
			}
5396
5397
			return new Jetpack_Error( (string) $registration_response->error, $error_description, $code );
5398
		} elseif ( 200 != $code ) {
5399
			return new Jetpack_Error( 'wpcom_bad_response', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5400
		}
5401
5402
		// Jetpack ID error block
5403
		if ( empty( $registration_response->jetpack_id ) ) {
5404
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack' ), $entity ), $entity );
5405
		} elseif ( ! is_scalar( $registration_response->jetpack_id ) ) {
5406
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is not a scalar. Do not publicly post this error message! %s', 'jetpack' ) , $entity ), $entity );
5407
		} elseif ( preg_match( '/[^0-9]/', $registration_response->jetpack_id ) ) {
5408
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID begins with a numeral. Do not publicly post this error message! %s', 'jetpack' ) , $entity ), $entity );
5409
		}
5410
5411
	    return $registration_response;
5412
	}
5413
	/**
5414
	 * @return bool|WP_Error
5415
	 */
5416
	public static function register() {
5417
		JetpackTracking::record_user_event( 'jpc_register_begin' );
5418
		add_action( 'pre_update_jetpack_option_register', array( 'Jetpack_Options', 'delete_option' ) );
5419
		$secrets = Jetpack::generate_secrets( 'register' );
5420
5421 View Code Duplication
		if (
5422
			empty( $secrets['secret_1'] ) ||
5423
			empty( $secrets['secret_2'] ) ||
5424
			empty( $secrets['exp'] )
5425
		) {
5426
			return new Jetpack_Error( 'missing_secrets' );
5427
		}
5428
5429
		// better to try (and fail) to set a higher timeout than this system
5430
		// supports than to have register fail for more users than it should
5431
		$timeout = Jetpack::set_min_time_limit( 60 ) / 2;
5432
5433
		$gmt_offset = get_option( 'gmt_offset' );
5434
		if ( ! $gmt_offset ) {
5435
			$gmt_offset = 0;
5436
		}
5437
5438
		$stats_options = get_option( 'stats_options' );
5439
		$stats_id = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
5440
5441
		$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
5442
5443
		$args = array(
5444
			'method'  => 'POST',
5445
			'body'    => array(
5446
				'siteurl'         => site_url(),
5447
				'home'            => home_url(),
5448
				'gmt_offset'      => $gmt_offset,
5449
				'timezone_string' => (string) get_option( 'timezone_string' ),
5450
				'site_name'       => (string) get_option( 'blogname' ),
5451
				'secret_1'        => $secrets['secret_1'],
5452
				'secret_2'        => $secrets['secret_2'],
5453
				'site_lang'       => get_locale(),
5454
				'timeout'         => $timeout,
5455
				'stats_id'        => $stats_id,
5456
				'state'           => get_current_user_id(),
5457
				'_ui'             => $tracks_identity['_ui'],
5458
				'_ut'             => $tracks_identity['_ut'],
5459
				'jetpack_version' => JETPACK__VERSION
5460
			),
5461
			'headers' => array(
5462
				'Accept' => 'application/json',
5463
			),
5464
			'timeout' => $timeout,
5465
		);
5466
5467
		self::apply_activation_source_to_args( $args['body'] );
5468
5469
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'register' ) ), $args, true );
5470
5471
		// Make sure the response is valid and does not contain any Jetpack errors
5472
		$registration_details = Jetpack::init()->validate_remote_register_response( $response );
5473
		if ( is_wp_error( $registration_details ) ) {
5474
			return $registration_details;
5475
		} elseif ( ! $registration_details ) {
5476
			return new Jetpack_Error( 'unknown_error', __( 'Unknown error registering your Jetpack site', 'jetpack' ), wp_remote_retrieve_response_code( $response ) );
5477
		}
5478
5479 View Code Duplication
		if ( empty( $registration_details->jetpack_secret ) || ! is_string( $registration_details->jetpack_secret ) ) {
5480
			return new Jetpack_Error( 'jetpack_secret', '', wp_remote_retrieve_response_code( $response ) );
5481
		}
5482
5483
		if ( isset( $registration_details->jetpack_public ) ) {
5484
			$jetpack_public = (int) $registration_details->jetpack_public;
5485
		} else {
5486
			$jetpack_public = false;
5487
		}
5488
5489
		Jetpack_Options::update_options(
5490
			array(
5491
				'id'         => (int)    $registration_details->jetpack_id,
5492
				'blog_token' => (string) $registration_details->jetpack_secret,
5493
				'public'     => $jetpack_public,
5494
			)
5495
		);
5496
5497
		/**
5498
		 * Fires when a site is registered on WordPress.com.
5499
		 *
5500
		 * @since 3.7.0
5501
		 *
5502
		 * @param int $json->jetpack_id Jetpack Blog ID.
5503
		 * @param string $json->jetpack_secret Jetpack Blog Token.
5504
		 * @param int|bool $jetpack_public Is the site public.
5505
		 */
5506
		do_action( 'jetpack_site_registered', $registration_details->jetpack_id, $registration_details->jetpack_secret, $jetpack_public );
5507
5508
		// Initialize Jump Start for the first and only time.
5509
		if ( ! Jetpack_Options::get_option( 'jumpstart' ) ) {
5510
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
5511
5512
			$jetpack = Jetpack::init();
5513
5514
			$jetpack->stat( 'jumpstart', 'unique-views' );
5515
			$jetpack->do_stats( 'server_side' );
5516
		};
5517
5518
		return true;
5519
	}
5520
5521
	/**
5522
	 * If the db version is showing something other that what we've got now, bump it to current.
5523
	 *
5524
	 * @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...
5525
	 */
5526
	public static function maybe_set_version_option() {
5527
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5528
		if ( JETPACK__VERSION != $version ) {
5529
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5530
5531
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5532
				/** This action is documented in class.jetpack.php */
5533
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5534
			}
5535
5536
			return true;
5537
		}
5538
		return false;
5539
	}
5540
5541
/* Client Server API */
5542
5543
	/**
5544
	 * Loads the Jetpack XML-RPC client
5545
	 */
5546
	public static function load_xml_rpc_client() {
5547
		require_once ABSPATH . WPINC . '/class-IXR.php';
5548
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-ixr-client.php';
5549
	}
5550
5551
	/**
5552
	 * Resets the saved authentication state in between testing requests.
5553
	 */
5554
	public function reset_saved_auth_state() {
5555
		$this->xmlrpc_verification = null;
5556
		$this->rest_authentication_status = null;
5557
	}
5558
5559
	function verify_xml_rpc_signature() {
5560
		if ( $this->xmlrpc_verification ) {
5561
			return $this->xmlrpc_verification;
5562
		}
5563
5564
		// It's not for us
5565
		if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
5566
			return false;
5567
		}
5568
5569
		@list( $token_key, $version, $user_id ) = explode( ':', $_GET['token'] );
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...
5570
		if (
5571
			empty( $token_key )
5572
		||
5573
			empty( $version ) || strval( JETPACK__API_VERSION ) !== $version
5574
		) {
5575
			return false;
5576
		}
5577
5578
		if ( '0' === $user_id ) {
5579
			$token_type = 'blog';
5580
			$user_id = 0;
5581
		} else {
5582
			$token_type = 'user';
5583
			if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
5584
				return false;
5585
			}
5586
			$user_id = (int) $user_id;
5587
5588
			$user = new WP_User( $user_id );
5589
			if ( ! $user || ! $user->exists() ) {
5590
				return false;
5591
			}
5592
		}
5593
5594
		$token = Jetpack_Data::get_access_token( $user_id );
0 ignored issues
show
Documentation introduced by
$user_id 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...
5595
		if ( ! $token ) {
5596
			return false;
5597
		}
5598
5599
		$token_check = "$token_key.";
5600
		if ( ! hash_equals( substr( $token->secret, 0, strlen( $token_check ) ), $token_check ) ) {
5601
			return false;
5602
		}
5603
5604
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
5605
5606
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5607
		if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
5608
			$post_data   = $_POST;
5609
			$file_hashes = array();
5610
			foreach ( $post_data as $post_data_key => $post_data_value ) {
5611
				if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
5612
					continue;
5613
				}
5614
				$post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
5615
				$file_hashes[$post_data_key] = $post_data_value;
5616
			}
5617
5618
			foreach ( $file_hashes as $post_data_key => $post_data_value ) {
5619
				unset( $post_data["_jetpack_file_hmac_{$post_data_key}"] );
5620
				$post_data[$post_data_key] = $post_data_value;
5621
			}
5622
5623
			ksort( $post_data );
5624
5625
			$body = http_build_query( stripslashes_deep( $post_data ) );
5626
		} elseif ( is_null( $this->HTTP_RAW_POST_DATA ) ) {
5627
			$body = file_get_contents( 'php://input' );
5628
		} else {
5629
			$body = null;
5630
		}
5631
5632
		$signature = $jetpack_signature->sign_current_request(
5633
			array( 'body' => is_null( $body ) ? $this->HTTP_RAW_POST_DATA : $body, )
5634
		);
5635
5636
		if ( ! $signature ) {
5637
			return false;
5638
		} else if ( is_wp_error( $signature ) ) {
5639
			return $signature;
5640
		} else if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
5641
			return false;
5642
		}
5643
5644
		$timestamp = (int) $_GET['timestamp'];
5645
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5646
5647
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5648
			return false;
5649
		}
5650
5651
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5652
		if ( isset( $this->HTTP_RAW_POST_DATA ) || ! empty( $_GET['onboarding'] ) ) {
5653
			if ( ! empty( $_GET['onboarding'] ) ) {
5654
				$jpo = $_GET;
5655
			} else {
5656
				$jpo = json_decode( $this->HTTP_RAW_POST_DATA, true );
5657
			}
5658
5659
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5660
			$jpo_user = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5661
5662
			if (
5663
				isset( $jpo_user ) && isset( $jpo_token ) &&
5664
				is_email( $jpo_user ) && ctype_alnum( $jpo_token ) &&
5665
				isset( $_GET['rest_route'] ) &&
5666
				self::validate_onboarding_token_action( $jpo_token, $_GET['rest_route'] )
5667
			) {
5668
				$jpUser = get_user_by( 'email', $jpo_user );
5669
				if ( is_a( $jpUser, 'WP_User' ) ) {
5670
					wp_set_current_user( $jpUser->ID );
5671
					$user_can = is_multisite()
5672
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5673
						: current_user_can( 'manage_options' );
5674
					if ( $user_can ) {
5675
						$token_type = 'user';
5676
						$token->external_user_id = $jpUser->ID;
5677
					}
5678
				}
5679
			}
5680
		}
5681
5682
		$this->xmlrpc_verification = array(
5683
			'type'    => $token_type,
5684
			'user_id' => $token->external_user_id,
5685
		);
5686
5687
		return $this->xmlrpc_verification;
5688
	}
5689
5690
	/**
5691
	 * Authenticates XML-RPC and other requests from the Jetpack Server
5692
	 */
5693
	function authenticate_jetpack( $user, $username, $password ) {
5694
		if ( is_a( $user, 'WP_User' ) ) {
5695
			return $user;
5696
		}
5697
5698
		$token_details = $this->verify_xml_rpc_signature();
5699
5700
		if ( ! $token_details || is_wp_error( $token_details ) ) {
5701
			return $user;
5702
		}
5703
5704
		if ( 'user' !== $token_details['type'] ) {
5705
			return $user;
5706
		}
5707
5708
		if ( ! $token_details['user_id'] ) {
5709
			return $user;
5710
		}
5711
5712
		nocache_headers();
5713
5714
		return new WP_User( $token_details['user_id'] );
5715
	}
5716
5717
	// Authenticates requests from Jetpack server to WP REST API endpoints.
5718
	// Uses the existing XMLRPC request signing implementation.
5719
	function wp_rest_authenticate( $user ) {
5720
		if ( ! empty( $user ) ) {
5721
			// Another authentication method is in effect.
5722
			return $user;
5723
		}
5724
5725
		if ( ! isset( $_GET['_for'] ) || $_GET['_for'] !== 'jetpack' ) {
5726
			// Nothing to do for this authentication method.
5727
			return null;
5728
		}
5729
5730
		if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
5731
			// Nothing to do for this authentication method.
5732
			return null;
5733
		}
5734
5735
		// Ensure that we always have the request body available.  At this
5736
		// point, the WP REST API code to determine the request body has not
5737
		// run yet.  That code may try to read from 'php://input' later, but
5738
		// this can only be done once per request in PHP versions prior to 5.6.
5739
		// So we will go ahead and perform this read now if needed, and save
5740
		// the request body where both the Jetpack signature verification code
5741
		// and the WP REST API code can see it.
5742
		if ( ! isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ) {
5743
			$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents( 'php://input' );
5744
		}
5745
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5746
5747
		// Only support specific request parameters that have been tested and
5748
		// are known to work with signature verification.  A different method
5749
		// can be passed to the WP REST API via the '?_method=' parameter if
5750
		// needed.
5751
		if ( $_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
5752
			$this->rest_authentication_status = new WP_Error(
5753
				'rest_invalid_request',
5754
				__( 'This request method is not supported.', 'jetpack' ),
5755
				array( 'status' => 400 )
5756
			);
5757
			return null;
5758
		}
5759
		if ( $_SERVER['REQUEST_METHOD'] !== 'POST' && ! empty( $this->HTTP_RAW_POST_DATA ) ) {
5760
			$this->rest_authentication_status = new WP_Error(
5761
				'rest_invalid_request',
5762
				__( 'This request method does not support body parameters.', 'jetpack' ),
5763
				array( 'status' => 400 )
5764
			);
5765
			return null;
5766
		}
5767
5768
		$verified = $this->verify_xml_rpc_signature();
5769
5770
		if ( is_wp_error( $verified ) ) {
5771
			$this->rest_authentication_status = $verified;
5772
			return null;
5773
		}
5774
5775
		if (
5776
			$verified &&
5777
			isset( $verified['type'] ) &&
5778
			'user' === $verified['type'] &&
5779
			! empty( $verified['user_id'] )
5780
		) {
5781
			// Authentication successful.
5782
			$this->rest_authentication_status = true;
5783
			return $verified['user_id'];
5784
		}
5785
5786
		// Something else went wrong.  Probably a signature error.
5787
		$this->rest_authentication_status = new WP_Error(
5788
			'rest_invalid_signature',
5789
			__( 'The request is not signed correctly.', 'jetpack' ),
5790
			array( 'status' => 400 )
5791
		);
5792
		return null;
5793
	}
5794
5795
	/**
5796
	 * Report authentication status to the WP REST API.
5797
	 *
5798
	 * @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...
5799
	 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5800
	 */
5801
	public function wp_rest_authentication_errors( $value ) {
5802
		if ( $value !== null ) {
5803
			return $value;
5804
		}
5805
		return $this->rest_authentication_status;
5806
	}
5807
5808
	function add_nonce( $timestamp, $nonce ) {
5809
		global $wpdb;
5810
		static $nonces_used_this_request = array();
5811
5812
		if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) {
5813
			return $nonces_used_this_request["$timestamp:$nonce"];
5814
		}
5815
5816
		// This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce
5817
		$timestamp = (int) $timestamp;
5818
		$nonce     = esc_sql( $nonce );
5819
5820
		// Raw query so we can avoid races: add_option will also update
5821
		$show_errors = $wpdb->show_errors( false );
5822
5823
		$old_nonce = $wpdb->get_row(
5824
			$wpdb->prepare( "SELECT * FROM `$wpdb->options` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
5825
		);
5826
5827
		if ( is_null( $old_nonce ) ) {
5828
			$return = $wpdb->query(
5829
				$wpdb->prepare(
5830
					"INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
5831
					"jetpack_nonce_{$timestamp}_{$nonce}",
5832
					time(),
5833
					'no'
5834
				)
5835
			);
5836
		} else {
5837
			$return = false;
5838
		}
5839
5840
		$wpdb->show_errors( $show_errors );
5841
5842
		$nonces_used_this_request["$timestamp:$nonce"] = $return;
5843
5844
		return $return;
5845
	}
5846
5847
	/**
5848
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5849
	 * Capture it here so we can verify the signature later.
5850
	 */
5851
	function xmlrpc_methods( $methods ) {
5852
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5853
		return $methods;
5854
	}
5855
5856
	function public_xmlrpc_methods( $methods ) {
5857
		if ( array_key_exists( 'wp.getOptions', $methods ) ) {
5858
			$methods['wp.getOptions'] = array( $this, 'jetpack_getOptions' );
5859
		}
5860
		return $methods;
5861
	}
5862
5863
	function jetpack_getOptions( $args ) {
5864
		global $wp_xmlrpc_server;
5865
5866
		$wp_xmlrpc_server->escape( $args );
5867
5868
		$username	= $args[1];
5869
		$password	= $args[2];
5870
5871
		if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
5872
			return $wp_xmlrpc_server->error;
5873
		}
5874
5875
		$options = array();
5876
		$user_data = $this->get_connected_user_data();
5877
		if ( is_array( $user_data ) ) {
5878
			$options['jetpack_user_id'] = array(
5879
				'desc'          => __( 'The WP.com user ID of the connected user', 'jetpack' ),
5880
				'readonly'      => true,
5881
				'value'         => $user_data['ID'],
5882
			);
5883
			$options['jetpack_user_login'] = array(
5884
				'desc'          => __( 'The WP.com username of the connected user', 'jetpack' ),
5885
				'readonly'      => true,
5886
				'value'         => $user_data['login'],
5887
			);
5888
			$options['jetpack_user_email'] = array(
5889
				'desc'          => __( 'The WP.com user email of the connected user', 'jetpack' ),
5890
				'readonly'      => true,
5891
				'value'         => $user_data['email'],
5892
			);
5893
			$options['jetpack_user_site_count'] = array(
5894
				'desc'          => __( 'The number of sites of the connected WP.com user', 'jetpack' ),
5895
				'readonly'      => true,
5896
				'value'         => $user_data['site_count'],
5897
			);
5898
		}
5899
		$wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
5900
		$args = stripslashes_deep( $args );
5901
		return $wp_xmlrpc_server->wp_getOptions( $args );
5902
	}
5903
5904
	function xmlrpc_options( $options ) {
5905
		$jetpack_client_id = false;
5906
		if ( self::is_active() ) {
5907
			$jetpack_client_id = Jetpack_Options::get_option( 'id' );
5908
		}
5909
		$options['jetpack_version'] = array(
5910
				'desc'          => __( 'Jetpack Plugin Version', 'jetpack' ),
5911
				'readonly'      => true,
5912
				'value'         => JETPACK__VERSION,
5913
		);
5914
5915
		$options['jetpack_client_id'] = array(
5916
				'desc'          => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack' ),
5917
				'readonly'      => true,
5918
				'value'         => $jetpack_client_id,
5919
		);
5920
		return $options;
5921
	}
5922
5923
	public static function clean_nonces( $all = false ) {
5924
		global $wpdb;
5925
5926
		$sql = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE %s";
5927
		$sql_args = array( $wpdb->esc_like( 'jetpack_nonce_' ) . '%' );
5928
5929
		if ( true !== $all ) {
5930
			$sql .= ' AND CAST( `option_value` AS UNSIGNED ) < %d';
5931
			$sql_args[] = time() - 3600;
5932
		}
5933
5934
		$sql .= ' ORDER BY `option_id` LIMIT 100';
5935
5936
		$sql = $wpdb->prepare( $sql, $sql_args );
5937
5938
		for ( $i = 0; $i < 1000; $i++ ) {
5939
			if ( ! $wpdb->query( $sql ) ) {
5940
				break;
5941
			}
5942
		}
5943
	}
5944
5945
	/**
5946
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5947
	 * SET: state( $key, $value );
5948
	 * GET: $value = state( $key );
5949
	 *
5950
	 * @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...
5951
	 * @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...
5952
	 * @param bool $restate private
5953
	 */
5954
	public static function state( $key = null, $value = null, $restate = false ) {
5955
		static $state = array();
5956
		static $path, $domain;
5957
		if ( ! isset( $path ) ) {
5958
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
5959
			$admin_url = Jetpack::admin_url();
5960
			$bits      = parse_url( $admin_url );
5961
5962
			if ( is_array( $bits ) ) {
5963
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5964
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5965
			} else {
5966
				$path = $domain = null;
5967
			}
5968
		}
5969
5970
		// Extract state from cookies and delete cookies
5971
		if ( isset( $_COOKIE[ 'jetpackState' ] ) && is_array( $_COOKIE[ 'jetpackState' ] ) ) {
5972
			$yum = $_COOKIE[ 'jetpackState' ];
5973
			unset( $_COOKIE[ 'jetpackState' ] );
5974
			foreach ( $yum as $k => $v ) {
5975
				if ( strlen( $v ) )
5976
					$state[ $k ] = $v;
5977
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5978
			}
5979
		}
5980
5981
		if ( $restate ) {
5982
			foreach ( $state as $k => $v ) {
5983
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5984
			}
5985
			return;
5986
		}
5987
5988
		// Get a state variable
5989
		if ( isset( $key ) && ! isset( $value ) ) {
5990
			if ( array_key_exists( $key, $state ) )
5991
				return $state[ $key ];
5992
			return null;
5993
		}
5994
5995
		// Set a state variable
5996
		if ( isset ( $key ) && isset( $value ) ) {
5997
			if( is_array( $value ) && isset( $value[0] ) ) {
5998
				$value = $value[0];
5999
			}
6000
			$state[ $key ] = $value;
6001
			setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
6002
		}
6003
	}
6004
6005
	public static function restate() {
6006
		Jetpack::state( null, null, true );
6007
	}
6008
6009
	public static function check_privacy( $file ) {
6010
		static $is_site_publicly_accessible = null;
6011
6012
		if ( is_null( $is_site_publicly_accessible ) ) {
6013
			$is_site_publicly_accessible = false;
6014
6015
			Jetpack::load_xml_rpc_client();
6016
			$rpc = new Jetpack_IXR_Client();
6017
6018
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
6019
			if ( $success ) {
6020
				$response = $rpc->getResponse();
6021
				if ( $response ) {
6022
					$is_site_publicly_accessible = true;
6023
				}
6024
			}
6025
6026
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
6027
		}
6028
6029
		if ( $is_site_publicly_accessible ) {
6030
			return;
6031
		}
6032
6033
		$module_slug = self::get_module_slug( $file );
6034
6035
		$privacy_checks = Jetpack::state( 'privacy_checks' );
6036
		if ( ! $privacy_checks ) {
6037
			$privacy_checks = $module_slug;
6038
		} else {
6039
			$privacy_checks .= ",$module_slug";
6040
		}
6041
6042
		Jetpack::state( 'privacy_checks', $privacy_checks );
6043
	}
6044
6045
	/**
6046
	 * Helper method for multicall XMLRPC.
6047
	 */
6048
	public static function xmlrpc_async_call() {
6049
		global $blog_id;
6050
		static $clients = array();
6051
6052
		$client_blog_id = is_multisite() ? $blog_id : 0;
6053
6054
		if ( ! isset( $clients[$client_blog_id] ) ) {
6055
			Jetpack::load_xml_rpc_client();
6056
			$clients[$client_blog_id] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER, ) );
6057
			if ( function_exists( 'ignore_user_abort' ) ) {
6058
				ignore_user_abort( true );
6059
			}
6060
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
6061
		}
6062
6063
		$args = func_get_args();
6064
6065
		if ( ! empty( $args[0] ) ) {
6066
			call_user_func_array( array( $clients[$client_blog_id], 'addCall' ), $args );
6067
		} elseif ( is_multisite() ) {
6068
			foreach ( $clients as $client_blog_id => $client ) {
6069
				if ( ! $client_blog_id || empty( $client->calls ) ) {
6070
					continue;
6071
				}
6072
6073
				$switch_success = switch_to_blog( $client_blog_id, true );
6074
				if ( ! $switch_success ) {
6075
					continue;
6076
				}
6077
6078
				flush();
6079
				$client->query();
6080
6081
				restore_current_blog();
6082
			}
6083
		} else {
6084
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
6085
				flush();
6086
				$clients[0]->query();
6087
			}
6088
		}
6089
	}
6090
6091
	public static function staticize_subdomain( $url ) {
6092
6093
		// Extract hostname from URL
6094
		$host = parse_url( $url, PHP_URL_HOST );
6095
6096
		// Explode hostname on '.'
6097
		$exploded_host = explode( '.', $host );
6098
6099
		// Retrieve the name and TLD
6100
		if ( count( $exploded_host ) > 1 ) {
6101
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
6102
			$tld = $exploded_host[ count( $exploded_host ) - 1 ];
6103
			// Rebuild domain excluding subdomains
6104
			$domain = $name . '.' . $tld;
6105
		} else {
6106
			$domain = $host;
6107
		}
6108
		// Array of Automattic domains
6109
		$domain_whitelist = array( 'wordpress.com', 'wp.com' );
6110
6111
		// Return $url if not an Automattic domain
6112
		if ( ! in_array( $domain, $domain_whitelist ) ) {
6113
			return $url;
6114
		}
6115
6116
		if ( is_ssl() ) {
6117
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
6118
		}
6119
6120
		srand( crc32( basename( $url ) ) );
6121
		$static_counter = rand( 0, 2 );
6122
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
6123
6124
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
6125
	}
6126
6127
/* JSON API Authorization */
6128
6129
	/**
6130
	 * Handles the login action for Authorizing the JSON API
6131
	 */
6132
	function login_form_json_api_authorization() {
6133
		$this->verify_json_api_authorization_request();
6134
6135
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
6136
6137
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
6138
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
6139
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
6140
	}
6141
6142
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
6143
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
6144
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
6145
			return $url;
6146
		}
6147
6148
		$parsed_url = parse_url( $url );
6149
		$url = strtok( $url, '?' );
6150
		$url = "$url?{$_SERVER['QUERY_STRING']}";
6151
		if ( ! empty( $parsed_url['query'] ) )
6152
			$url .= "&{$parsed_url['query']}";
6153
6154
		return $url;
6155
	}
6156
6157
	// Make sure the POSTed request is handled by the same action
6158
	function preserve_action_in_login_form_for_json_api_authorization() {
6159
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
6160
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
6161
	}
6162
6163
	// If someone logs in to approve API access, store the Access Code in usermeta
6164
	function store_json_api_authorization_token( $user_login, $user ) {
6165
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
6166
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
6167
		$token = wp_generate_password( 32, false );
6168
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
6169
	}
6170
6171
	// Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access
6172
	function allow_wpcom_public_api_domain( $domains ) {
6173
		$domains[] = 'public-api.wordpress.com';
6174
		return $domains;
6175
	}
6176
6177
	// Add all wordpress.com environments to the safe redirect whitelist
6178
	function allow_wpcom_environments( $domains ) {
6179
		$domains[] = 'wordpress.com';
6180
		$domains[] = 'wpcalypso.wordpress.com';
6181
		$domains[] = 'horizon.wordpress.com';
6182
		$domains[] = 'calypso.localhost';
6183
		return $domains;
6184
	}
6185
6186
	// Add the Access Code details to the public-api.wordpress.com redirect
6187
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
6188
		return add_query_arg(
6189
			urlencode_deep(
6190
				array(
6191
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
6192
					'jetpack-user-id' => (int) $user->ID,
6193
					'jetpack-state'   => $this->json_api_authorization_request['state'],
6194
				)
6195
			),
6196
			$redirect_to
6197
		);
6198
	}
6199
6200
6201
	/**
6202
	 * Verifies the request by checking the signature
6203
	 *
6204
	 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
6205
	 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
6206
	 *
6207
	 * @param null|array $environment
6208
	 */
6209
	function verify_json_api_authorization_request( $environment = null ) {
6210
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
6211
6212
		$environment = is_null( $environment )
6213
			? $_REQUEST
6214
			: $environment;
6215
6216
		list( $envToken, $envVersion, $envUserId ) = explode( ':', $environment['token'] );
0 ignored issues
show
Unused Code introduced by
The assignment to $envToken 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...
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...
6217
		$token = Jetpack_Data::get_access_token( $envUserId );
6218
		if ( ! $token || empty( $token->secret ) ) {
6219
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' , 'jetpack' ) );
6220
		}
6221
6222
		$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' );
6223
6224
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
6225
6226
		if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
6227
			$signature = $jetpack_signature->sign_request(
6228
				$environment['token'],
6229
				$environment['timestamp'],
6230
				$environment['nonce'],
6231
				'',
6232
				'GET',
6233
				$environment['jetpack_json_api_original_query'],
6234
				null,
6235
				true
6236
			);
6237
		} else {
6238
			$signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) );
6239
		}
6240
6241
		if ( ! $signature ) {
6242
			wp_die( $die_error );
6243
		} else if ( is_wp_error( $signature ) ) {
6244
			wp_die( $die_error );
6245
		} else if ( ! hash_equals( $signature, $environment['signature'] ) ) {
6246
			if ( is_ssl() ) {
6247
				// 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
6248
				$signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) );
6249
				if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
6250
					wp_die( $die_error );
6251
				}
6252
			} else {
6253
				wp_die( $die_error );
6254
			}
6255
		}
6256
6257
		$timestamp = (int) $environment['timestamp'];
6258
		$nonce     = stripslashes( (string) $environment['nonce'] );
6259
6260
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
6261
			// De-nonce the nonce, at least for 5 minutes.
6262
			// 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)
6263
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
6264
			if ( $old_nonce_time < time() - 300 ) {
6265
				wp_die( __( 'The authorization process expired.  Please go back and try again.' , 'jetpack' ) );
6266
			}
6267
		}
6268
6269
		$data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
6270
		$data_filters = array(
6271
			'state'        => 'opaque',
6272
			'client_id'    => 'int',
6273
			'client_title' => 'string',
6274
			'client_image' => 'url',
6275
		);
6276
6277
		foreach ( $data_filters as $key => $sanitation ) {
6278
			if ( ! isset( $data->$key ) ) {
6279
				wp_die( $die_error );
6280
			}
6281
6282
			switch ( $sanitation ) {
6283
			case 'int' :
6284
				$this->json_api_authorization_request[$key] = (int) $data->$key;
6285
				break;
6286
			case 'opaque' :
6287
				$this->json_api_authorization_request[$key] = (string) $data->$key;
6288
				break;
6289
			case 'string' :
6290
				$this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() );
6291
				break;
6292
			case 'url' :
6293
				$this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key );
6294
				break;
6295
			}
6296
		}
6297
6298
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
6299
			wp_die( $die_error );
6300
		}
6301
	}
6302
6303
	function login_message_json_api_authorization( $message ) {
6304
		return '<p class="message">' . sprintf(
6305
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' , 'jetpack' ),
6306
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
6307
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
6308
	}
6309
6310
	/**
6311
	 * Get $content_width, but with a <s>twist</s> filter.
6312
	 */
6313
	public static function get_content_width() {
6314
		$content_width = isset( $GLOBALS['content_width'] ) ? $GLOBALS['content_width'] : false;
6315
		/**
6316
		 * Filter the Content Width value.
6317
		 *
6318
		 * @since 2.2.3
6319
		 *
6320
		 * @param string $content_width Content Width value.
6321
		 */
6322
		return apply_filters( 'jetpack_content_width', $content_width );
6323
	}
6324
6325
	/**
6326
	 * Pings the WordPress.com Mirror Site for the specified options.
6327
	 *
6328
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6329
	 *
6330
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6331
	 */
6332
	public function get_cloud_site_options( $option_names ) {
6333
		$option_names = array_filter( (array) $option_names, 'is_string' );
6334
6335
		Jetpack::load_xml_rpc_client();
6336
		$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
6337
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
6338
		if ( $xml->isError() ) {
6339
			return array(
6340
				'error_code' => $xml->getErrorCode(),
6341
				'error_msg'  => $xml->getErrorMessage(),
6342
			);
6343
		}
6344
		$cloud_site_options = $xml->getResponse();
6345
6346
		return $cloud_site_options;
6347
	}
6348
6349
	/**
6350
	 * Checks if the site is currently in an identity crisis.
6351
	 *
6352
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6353
	 */
6354
	public static function check_identity_crisis() {
6355
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || ! self::validate_sync_error_idc_option() ) {
6356
			return false;
6357
		}
6358
6359
		return Jetpack_Options::get_option( 'sync_error_idc' );
6360
	}
6361
6362
	/**
6363
	 * Checks whether the home and siteurl specifically are whitelisted
6364
	 * Written so that we don't have re-check $key and $value params every time
6365
	 * we want to check if this site is whitelisted, for example in footer.php
6366
	 *
6367
	 * @since  3.8.0
6368
	 * @return bool True = already whitelisted False = not whitelisted
6369
	 */
6370
	public static function is_staging_site() {
6371
		$is_staging = false;
6372
6373
		$known_staging = array(
6374
			'urls' => array(
6375
				'#\.staging\.wpengine\.com$#i', // WP Engine
6376
				'#\.staging\.kinsta\.com$#i',   // Kinsta.com
6377
				),
6378
			'constants' => array(
6379
				'IS_WPE_SNAPSHOT',      // WP Engine
6380
				'KINSTA_DEV_ENV',       // Kinsta.com
6381
				'WPSTAGECOACH_STAGING', // WP Stagecoach
6382
				'JETPACK_STAGING_MODE', // Generic
6383
				)
6384
			);
6385
		/**
6386
		 * Filters the flags of known staging sites.
6387
		 *
6388
		 * @since 3.9.0
6389
		 *
6390
		 * @param array $known_staging {
6391
		 *     An array of arrays that each are used to check if the current site is staging.
6392
		 *     @type array $urls      URLs of staging sites in regex to check against site_url.
6393
		 *     @type array $constants PHP constants of known staging/developement environments.
6394
		 *  }
6395
		 */
6396
		$known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
6397
6398
		if ( isset( $known_staging['urls'] ) ) {
6399
			foreach ( $known_staging['urls'] as $url ){
6400
				if ( preg_match( $url, site_url() ) ) {
6401
					$is_staging = true;
6402
					break;
6403
				}
6404
			}
6405
		}
6406
6407
		if ( isset( $known_staging['constants'] ) ) {
6408
			foreach ( $known_staging['constants'] as $constant ) {
6409
				if ( defined( $constant ) && constant( $constant ) ) {
6410
					$is_staging = true;
6411
				}
6412
			}
6413
		}
6414
6415
		// Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
6416
		if ( ! $is_staging && self::validate_sync_error_idc_option() ) {
6417
			$is_staging = true;
6418
		}
6419
6420
		/**
6421
		 * Filters is_staging_site check.
6422
		 *
6423
		 * @since 3.9.0
6424
		 *
6425
		 * @param bool $is_staging If the current site is a staging site.
6426
		 */
6427
		return apply_filters( 'jetpack_is_staging_site', $is_staging );
6428
	}
6429
6430
	/**
6431
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6432
	 *
6433
	 * @since 4.4.0
6434
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6435
	 *
6436
	 * @return bool
6437
	 */
6438
	public static function validate_sync_error_idc_option() {
6439
		$is_valid = false;
6440
6441
		$idc_allowed = get_transient( 'jetpack_idc_allowed' );
6442
		if ( false === $idc_allowed ) {
6443
			$response = wp_remote_get( 'https://jetpack.com/is-idc-allowed/' );
6444
			if ( 200 === (int) wp_remote_retrieve_response_code( $response ) ) {
6445
				$json = json_decode( wp_remote_retrieve_body( $response ) );
6446
				$idc_allowed = isset( $json, $json->result ) && $json->result ? '1' : '0';
6447
				$transient_duration = HOUR_IN_SECONDS;
6448
			} else {
6449
				// If the request failed for some reason, then assume IDC is allowed and set shorter transient.
6450
				$idc_allowed = '1';
6451
				$transient_duration = 5 * MINUTE_IN_SECONDS;
6452
			}
6453
6454
			set_transient( 'jetpack_idc_allowed', $idc_allowed, $transient_duration );
6455
		}
6456
6457
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
6458
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
6459
		if ( $idc_allowed && $sync_error && self::sync_idc_optin() ) {
6460
			$local_options = self::get_sync_error_idc_option();
6461
			if ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
6462
				$is_valid = true;
6463
			}
6464
		}
6465
6466
		/**
6467
		 * Filters whether the sync_error_idc option is valid.
6468
		 *
6469
		 * @since 4.4.0
6470
		 *
6471
		 * @param bool $is_valid If the sync_error_idc is valid or not.
6472
		 */
6473
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
6474
6475
		if ( ! $idc_allowed || ( ! $is_valid && $sync_error ) ) {
6476
			// Since the option exists, and did not validate, delete it
6477
			Jetpack_Options::delete_option( 'sync_error_idc' );
6478
		}
6479
6480
		return $is_valid;
6481
	}
6482
6483
	/**
6484
	 * Normalizes a url by doing three things:
6485
	 *  - Strips protocol
6486
	 *  - Strips www
6487
	 *  - Adds a trailing slash
6488
	 *
6489
	 * @since 4.4.0
6490
	 * @param string $url
6491
	 * @return WP_Error|string
6492
	 */
6493
	public static function normalize_url_protocol_agnostic( $url ) {
6494
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6495
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
6496
			return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) );
6497
		}
6498
6499
		// Strip www and protocols
6500
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6501
		return $url;
6502
	}
6503
6504
	/**
6505
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6506
	 *
6507
	 * @since 4.4.0
6508
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6509
	 *
6510
	 * @param array $response
6511
	 * @return array Array of the local urls, wpcom urls, and error code
6512
	 */
6513
	public static function get_sync_error_idc_option( $response = array() ) {
6514
		// Since the local options will hit the database directly, store the values
6515
		// in a transient to allow for autoloading and caching on subsequent views.
6516
		$local_options = get_transient( 'jetpack_idc_local' );
6517
		if ( false === $local_options ) {
6518
			require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-functions.php';
6519
			$local_options = array(
6520
				'home'    => Jetpack_Sync_Functions::home_url(),
6521
				'siteurl' => Jetpack_Sync_Functions::site_url(),
6522
			);
6523
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
6524
		}
6525
6526
		$options = array_merge( $local_options, $response );
6527
6528
		$returned_values = array();
6529
		foreach( $options as $key => $option ) {
6530
			if ( 'error_code' === $key ) {
6531
				$returned_values[ $key ] = $option;
6532
				continue;
6533
			}
6534
6535
			if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) {
6536
				continue;
6537
			}
6538
6539
			$returned_values[ $key ] = $normalized_url;
6540
		}
6541
6542
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
6543
6544
		return $returned_values;
6545
	}
6546
6547
	/**
6548
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6549
	 * If set to true, the site will be put into staging mode.
6550
	 *
6551
	 * @since 4.3.2
6552
	 * @return bool
6553
	 */
6554
	public static function sync_idc_optin() {
6555
		if ( Jetpack_Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
6556
			$default = Jetpack_Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
6557
		} else {
6558
			$default = ! Jetpack_Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
6559
		}
6560
6561
		/**
6562
		 * Allows sites to optin to IDC mitigation which blocks the site from syncing to WordPress.com when the home
6563
		 * URL or site URL do not match what WordPress.com expects. The default value is either false, or the value of
6564
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
6565
		 *
6566
		 * @since 4.3.2
6567
		 *
6568
		 * @param bool $default Whether the site is opted in to IDC mitigation.
6569
		 */
6570
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
6571
	}
6572
6573
	/**
6574
	 * Maybe Use a .min.css stylesheet, maybe not.
6575
	 *
6576
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6577
	 */
6578
	public static function maybe_min_asset( $url, $path, $plugin ) {
6579
		// Short out on things trying to find actual paths.
6580
		if ( ! $path || empty( $plugin ) ) {
6581
			return $url;
6582
		}
6583
6584
		$path = ltrim( $path, '/' );
6585
6586
		// Strip out the abspath.
6587
		$base = dirname( plugin_basename( $plugin ) );
6588
6589
		// Short out on non-Jetpack assets.
6590
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6591
			return $url;
6592
		}
6593
6594
		// File name parsing.
6595
		$file              = "{$base}/{$path}";
6596
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6597
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6598
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6599
		$extension         = array_shift( $file_name_parts_r );
6600
6601
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6602
			// Already pointing at the minified version.
6603
			if ( 'min' === $file_name_parts_r[0] ) {
6604
				return $url;
6605
			}
6606
6607
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6608
			if ( file_exists( $min_full_path ) ) {
6609
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6610
				// If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6611
				if ( 'css' === $extension ) {
6612
					$key = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6613
					self::$min_assets[ $key ] = $path;
6614
				}
6615
			}
6616
		}
6617
6618
		return $url;
6619
	}
6620
6621
	/**
6622
	 * If the asset is minified, let's flag .min as the suffix.
6623
	 *
6624
	 * Attached to `style_loader_src` filter.
6625
	 *
6626
	 * @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...
6627
	 * @param string $handle The registered handle of the script in question.
6628
	 * @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...
6629
	 */
6630
	public static function set_suffix_on_min( $src, $handle ) {
6631
		if ( false === strpos( $src, '.min.css' ) ) {
6632
			return $src;
6633
		}
6634
6635
		if ( ! empty( self::$min_assets ) ) {
6636
			foreach ( self::$min_assets as $file => $path ) {
6637
				if ( false !== strpos( $src, $file ) ) {
6638
					wp_style_add_data( $handle, 'suffix', '.min' );
6639
					return $src;
6640
				}
6641
			}
6642
		}
6643
6644
		return $src;
6645
	}
6646
6647
	/**
6648
	 * Maybe inlines a stylesheet.
6649
	 *
6650
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6651
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6652
	 *
6653
	 * Attached to `style_loader_tag` filter.
6654
	 *
6655
	 * @param string $tag The tag that would link to the external asset.
6656
	 * @param string $handle The registered handle of the script in question.
6657
	 *
6658
	 * @return string
6659
	 */
6660
	public static function maybe_inline_style( $tag, $handle ) {
6661
		global $wp_styles;
6662
		$item = $wp_styles->registered[ $handle ];
6663
6664
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6665
			return $tag;
6666
		}
6667
6668
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6669
			$href = $matches[1];
6670
			// Strip off query string
6671
			if ( $pos = strpos( $href, '?' ) ) {
6672
				$href = substr( $href, 0, $pos );
6673
			}
6674
			// Strip off fragment
6675
			if ( $pos = strpos( $href, '#' ) ) {
6676
				$href = substr( $href, 0, $pos );
6677
			}
6678
		} else {
6679
			return $tag;
6680
		}
6681
6682
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6683
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6684
			return $tag;
6685
		}
6686
6687
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6688
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6689
			// And this isn't the pass that actually deals with the RTL version...
6690
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6691
				// Short out, as the RTL version will deal with it in a moment.
6692
				return $tag;
6693
			}
6694
		}
6695
6696
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6697
		$css  = Jetpack::absolutize_css_urls( file_get_contents( $file ), $href );
6698
		if ( $css ) {
6699
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6700
			if ( empty( $item->extra['after'] ) ) {
6701
				wp_add_inline_style( $handle, $css );
6702
			} else {
6703
				array_unshift( $item->extra['after'], $css );
6704
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6705
			}
6706
		}
6707
6708
		return $tag;
6709
	}
6710
6711
	/**
6712
	 * Loads a view file from the views
6713
	 *
6714
	 * Data passed in with the $data parameter will be available in the
6715
	 * template file as $data['value']
6716
	 *
6717
	 * @param string $template - Template file to load
6718
	 * @param array $data - Any data to pass along to the template
6719
	 * @return boolean - If template file was found
6720
	 **/
6721
	public function load_view( $template, $data = array() ) {
6722
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6723
6724
		if( file_exists( $views_dir . $template ) ) {
6725
			require_once( $views_dir . $template );
6726
			return true;
6727
		}
6728
6729
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6730
		return false;
6731
	}
6732
6733
	/**
6734
	 * Throws warnings for deprecated hooks to be removed from Jetpack
6735
	 */
6736
	public function deprecated_hooks() {
6737
		global $wp_filter;
6738
6739
		/*
6740
		 * Format:
6741
		 * deprecated_filter_name => replacement_name
6742
		 *
6743
		 * If there is no replacement, use null for replacement_name
6744
		 */
6745
		$deprecated_list = array(
6746
			'jetpack_bail_on_shortcode'                              => 'jetpack_shortcodes_to_include',
6747
			'wpl_sharing_2014_1'                                     => null,
6748
			'jetpack-tools-to-include'                               => 'jetpack_tools_to_include',
6749
			'jetpack_identity_crisis_options_to_check'               => null,
6750
			'update_option_jetpack_single_user_site'                 => null,
6751
			'audio_player_default_colors'                            => null,
6752
			'add_option_jetpack_featured_images_enabled'             => null,
6753
			'add_option_jetpack_update_details'                      => null,
6754
			'add_option_jetpack_updates'                             => null,
6755
			'add_option_jetpack_network_name'                        => null,
6756
			'add_option_jetpack_network_allow_new_registrations'     => null,
6757
			'add_option_jetpack_network_add_new_users'               => null,
6758
			'add_option_jetpack_network_site_upload_space'           => null,
6759
			'add_option_jetpack_network_upload_file_types'           => null,
6760
			'add_option_jetpack_network_enable_administration_menus' => null,
6761
			'add_option_jetpack_is_multi_site'                       => null,
6762
			'add_option_jetpack_is_main_network'                     => null,
6763
			'add_option_jetpack_main_network_site'                   => null,
6764
			'jetpack_sync_all_registered_options'                    => null,
6765
			'jetpack_has_identity_crisis'                            => 'jetpack_sync_error_idc_validation',
6766
			'jetpack_is_post_mailable'                               => null,
6767
			'jetpack_seo_site_host'                                  => null,
6768
			'jetpack_installed_plugin'                               => 'jetpack_plugin_installed',
6769
			'jetpack_holiday_snow_option_name'                       => null,
6770
			'jetpack_holiday_chance_of_snow'                         => null,
6771
			'jetpack_holiday_snow_js_url'                            => null,
6772
			'jetpack_is_holiday_snow_season'                         => null,
6773
			'jetpack_holiday_snow_option_updated'                    => null,
6774
			'jetpack_holiday_snowing'                                => null,
6775
			'jetpack_sso_auth_cookie_expirtation'                    => 'jetpack_sso_auth_cookie_expiration',
6776
			'jetpack_cache_plans'                                    => null,
6777
			'jetpack_updated_theme'                                  => 'jetpack_updated_themes',
6778
			'jetpack_lazy_images_skip_image_with_atttributes'        => 'jetpack_lazy_images_skip_image_with_attributes',
6779
			'jetpack_enable_site_verification'                       => null,
6780
		);
6781
6782
		// This is a silly loop depth. Better way?
6783
		foreach( $deprecated_list AS $hook => $hook_alt ) {
6784
			if ( has_action( $hook ) ) {
6785
				foreach( $wp_filter[ $hook ] AS $func => $values ) {
6786
					foreach( $values AS $hooked ) {
6787
						if ( is_callable( $hooked['function'] ) ) {
6788
							$function_name = 'an anonymous function';
6789
						} else {
6790
							$function_name = $hooked['function'];
6791
						}
6792
						_deprecated_function( $hook . ' used for ' . $function_name, null, $hook_alt );
6793
					}
6794
				}
6795
			}
6796
		}
6797
	}
6798
6799
	/**
6800
	 * Converts any url in a stylesheet, to the correct absolute url.
6801
	 *
6802
	 * Considerations:
6803
	 *  - Normal, relative URLs     `feh.png`
6804
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6805
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6806
	 *  - Absolute URLs             `http://domain.com/feh.png`
6807
	 *  - Domain root relative URLs `/feh.png`
6808
	 *
6809
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6810
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6811
	 *
6812
	 * @return mixed|string
6813
	 */
6814
	public static function absolutize_css_urls( $css, $css_file_url ) {
6815
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6816
		$css_dir = dirname( $css_file_url );
6817
		$p       = parse_url( $css_dir );
6818
		$domain  = sprintf(
6819
					'%1$s//%2$s%3$s%4$s',
6820
					isset( $p['scheme'] )           ? "{$p['scheme']}:" : '',
6821
					isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6822
					$p['host'],
6823
					isset( $p['port'] )             ? ":{$p['port']}" : ''
6824
				);
6825
6826
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6827
			$find = $replace = array();
6828
			foreach ( $matches as $match ) {
6829
				$url = trim( $match['path'], "'\" \t" );
6830
6831
				// If this is a data url, we don't want to mess with it.
6832
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6833
					continue;
6834
				}
6835
6836
				// If this is an absolute or protocol-agnostic url,
6837
				// we don't want to mess with it.
6838
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6839
					continue;
6840
				}
6841
6842
				switch ( substr( $url, 0, 1 ) ) {
6843
					case '/':
6844
						$absolute = $domain . $url;
6845
						break;
6846
					default:
6847
						$absolute = $css_dir . '/' . $url;
6848
				}
6849
6850
				$find[]    = $match[0];
6851
				$replace[] = sprintf( 'url("%s")', $absolute );
6852
			}
6853
			$css = str_replace( $find, $replace, $css );
6854
		}
6855
6856
		return $css;
6857
	}
6858
6859
	/**
6860
	 * This methods removes all of the registered css files on the front end
6861
	 * from Jetpack in favor of using a single file. In effect "imploding"
6862
	 * all the files into one file.
6863
	 *
6864
	 * Pros:
6865
	 * - Uses only ONE css asset connection instead of 15
6866
	 * - Saves a minimum of 56k
6867
	 * - Reduces server load
6868
	 * - Reduces time to first painted byte
6869
	 *
6870
	 * Cons:
6871
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6872
	 *		should not cause any issues with themes.
6873
	 * - Plugins/themes dequeuing styles no longer do anything. See
6874
	 *		jetpack_implode_frontend_css filter for a workaround
6875
	 *
6876
	 * For some situations developers may wish to disable css imploding and
6877
	 * instead operate in legacy mode where each file loads seperately and
6878
	 * can be edited individually or dequeued. This can be accomplished with
6879
	 * the following line:
6880
	 *
6881
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6882
	 *
6883
	 * @since 3.2
6884
	 **/
6885
	public function implode_frontend_css( $travis_test = false ) {
6886
		$do_implode = true;
6887
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6888
			$do_implode = false;
6889
		}
6890
6891
		// Do not implode CSS when the page loads via the AMP plugin.
6892
		if ( Jetpack_AMP_Support::is_amp_request() ) {
6893
			$do_implode = false;
6894
		}
6895
6896
		/**
6897
		 * Allow CSS to be concatenated into a single jetpack.css file.
6898
		 *
6899
		 * @since 3.2.0
6900
		 *
6901
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6902
		 */
6903
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6904
6905
		// Do not use the imploded file when default behavior was altered through the filter
6906
		if ( ! $do_implode ) {
6907
			return;
6908
		}
6909
6910
		// We do not want to use the imploded file in dev mode, or if not connected
6911
		if ( Jetpack::is_development_mode() || ! self::is_active() ) {
6912
			if ( ! $travis_test ) {
6913
				return;
6914
			}
6915
		}
6916
6917
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6918
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6919
			return;
6920
		}
6921
6922
		/*
6923
		 * Now we assume Jetpack is connected and able to serve the single
6924
		 * file.
6925
		 *
6926
		 * In the future there will be a check here to serve the file locally
6927
		 * or potentially from the Jetpack CDN
6928
		 *
6929
		 * For now:
6930
		 * - Enqueue a single imploded css file
6931
		 * - Zero out the style_loader_tag for the bundled ones
6932
		 * - Be happy, drink scotch
6933
		 */
6934
6935
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6936
6937
		$version = Jetpack::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6938
6939
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6940
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6941
	}
6942
6943
	function concat_remove_style_loader_tag( $tag, $handle ) {
6944
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6945
			$tag = '';
6946
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6947
				$tag = "<!-- `" . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6948
			}
6949
		}
6950
6951
		return $tag;
6952
	}
6953
6954
	/*
6955
	 * Check the heartbeat data
6956
	 *
6957
	 * Organizes the heartbeat data by severity.  For example, if the site
6958
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6959
	 *
6960
	 * Data will be added to "caution" array, if it either:
6961
	 *  - Out of date Jetpack version
6962
	 *  - Out of date WP version
6963
	 *  - Out of date PHP version
6964
	 *
6965
	 * $return array $filtered_data
6966
	 */
6967
	public static function jetpack_check_heartbeat_data() {
6968
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6969
6970
		$good    = array();
6971
		$caution = array();
6972
		$bad     = array();
6973
6974
		foreach ( $raw_data as $stat => $value ) {
6975
6976
			// Check jetpack version
6977
			if ( 'version' == $stat ) {
6978
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6979
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__VERSION;
6980
					continue;
6981
				}
6982
			}
6983
6984
			// Check WP version
6985
			if ( 'wp-version' == $stat ) {
6986
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6987
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_WP_VERSION;
6988
					continue;
6989
				}
6990
			}
6991
6992
			// Check PHP version
6993
			if ( 'php-version' == $stat ) {
6994
				if ( version_compare( PHP_VERSION, '5.2.4', '<' ) ) {
6995
					$caution[ $stat ] = $value . " - min supported is 5.2.4";
6996
					continue;
6997
				}
6998
			}
6999
7000
			// Check ID crisis
7001
			if ( 'identitycrisis' == $stat ) {
7002
				if ( 'yes' == $value ) {
7003
					$bad[ $stat ] = $value;
7004
					continue;
7005
				}
7006
			}
7007
7008
			// The rest are good :)
7009
			$good[ $stat ] = $value;
7010
		}
7011
7012
		$filtered_data = array(
7013
			'good'    => $good,
7014
			'caution' => $caution,
7015
			'bad'     => $bad
7016
		);
7017
7018
		return $filtered_data;
7019
	}
7020
7021
7022
	/*
7023
	 * This method is used to organize all options that can be reset
7024
	 * without disconnecting Jetpack.
7025
	 *
7026
	 * It is used in class.jetpack-cli.php to reset options
7027
	 *
7028
	 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
7029
	 *
7030
	 * @return array of options to delete.
7031
	 */
7032
	public static function get_jetpack_options_for_reset() {
7033
		return Jetpack_Options::get_options_for_reset();
7034
	}
7035
7036
	/**
7037
	 * Check if an option of a Jetpack module has been updated.
7038
	 *
7039
	 * If any module option has been updated before Jump Start has been dismissed,
7040
	 * update the 'jumpstart' option so we can hide Jump Start.
7041
	 *
7042
	 * @param string $option_name
7043
	 *
7044
	 * @return bool
7045
	 */
7046
	public static function jumpstart_has_updated_module_option( $option_name = '' ) {
7047
		// Bail if Jump Start has already been dismissed
7048
		if ( 'new_connection' !== Jetpack_Options::get_option( 'jumpstart' ) ) {
7049
			return false;
7050
		}
7051
7052
		$jetpack = Jetpack::init();
7053
7054
		// Manual build of module options
7055
		$option_names = self::get_jetpack_options_for_reset();
7056
7057
		if ( in_array( $option_name, $option_names['wp_options'] ) ) {
7058
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
7059
7060
			//Jump start is being dismissed send data to MC Stats
7061
			$jetpack->stat( 'jumpstart', 'manual,'.$option_name );
7062
7063
			$jetpack->do_stats( 'server_side' );
7064
		}
7065
7066
	}
7067
7068
	/*
7069
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
7070
	 * so we can bring them directly to their site in calypso.
7071
	 *
7072
	 * @param string | url
7073
	 * @return string | url without the guff
7074
	 */
7075
	public static function build_raw_urls( $url ) {
7076
		$strip_http = '/.*?:\/\//i';
7077
		$url = preg_replace( $strip_http, '', $url  );
7078
		$url = str_replace( '/', '::', $url );
7079
		return $url;
7080
	}
7081
7082
	/**
7083
	 * Stores and prints out domains to prefetch for page speed optimization.
7084
	 *
7085
	 * @param mixed $new_urls
7086
	 */
7087
	public static function dns_prefetch( $new_urls = null ) {
7088
		static $prefetch_urls = array();
7089
		if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) {
7090
			echo "\r\n";
7091
			foreach ( $prefetch_urls as $this_prefetch_url ) {
7092
				printf( "<link rel='dns-prefetch' href='%s'/>\r\n", esc_attr( $this_prefetch_url ) );
7093
			}
7094
		} elseif ( ! empty( $new_urls ) ) {
7095
			if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) {
7096
				add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) );
7097
			}
7098
			foreach ( (array) $new_urls as $this_new_url ) {
7099
				$prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) );
7100
			}
7101
			$prefetch_urls = array_unique( $prefetch_urls );
7102
		}
7103
	}
7104
7105
	public function wp_dashboard_setup() {
7106
		if ( self::is_active() ) {
7107
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
7108
		}
7109
7110
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
7111
			$widget_title = sprintf(
7112
				esc_html__( 'Stats by %s', 'jetpack' ),
7113
				Jetpack::get_jp_emblem( true )
7114
			);
7115
7116
			wp_add_dashboard_widget(
7117
				'jetpack_summary_widget',
7118
				$widget_title,
7119
				array( __CLASS__, 'dashboard_widget' )
7120
			);
7121
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
7122
7123
			// If we're inactive and not in development mode, sort our box to the top.
7124
			if ( ! self::is_active() && ! self::is_development_mode() ) {
7125
				global $wp_meta_boxes;
7126
7127
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
7128
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
7129
7130
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
7131
			}
7132
		}
7133
	}
7134
7135
	/**
7136
	 * @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...
7137
	 * @return mixed
7138
	 */
7139
	function get_user_option_meta_box_order_dashboard( $sorted ) {
7140
		if ( ! is_array( $sorted ) ) {
7141
			return $sorted;
7142
		}
7143
7144
		foreach ( $sorted as $box_context => $ids ) {
7145
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
7146
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
7147
				continue;
7148
			}
7149
7150
			$ids_array = explode( ',', $ids );
7151
			$key = array_search( 'dashboard_stats', $ids_array );
7152
7153
			if ( false !== $key ) {
7154
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
7155
				$ids_array[ $key ] = 'jetpack_summary_widget';
7156
				$sorted[ $box_context ] = implode( ',', $ids_array );
7157
				// We've found it, stop searching, and just return.
7158
				break;
7159
			}
7160
		}
7161
7162
		return $sorted;
7163
	}
7164
7165
	public static function dashboard_widget() {
7166
		/**
7167
		 * Fires when the dashboard is loaded.
7168
		 *
7169
		 * @since 3.4.0
7170
		 */
7171
		do_action( 'jetpack_dashboard_widget' );
7172
	}
7173
7174
	public static function dashboard_widget_footer() {
7175
		?>
7176
		<footer>
7177
7178
		<div class="protect">
7179
			<?php if ( Jetpack::is_module_active( 'protect' ) ) : ?>
7180
				<h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3>
7181
				<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>
7182
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! self::is_development_mode() ) : ?>
7183
				<a href="<?php echo esc_url( wp_nonce_url( Jetpack::admin_url( array( 'action' => 'activate', 'module' => 'protect' ) ), 'jetpack_activate-protect' ) ); ?>" class="button button-jetpack" title="<?php esc_attr_e( 'Protect helps to keep you secure from brute-force login attacks.', 'jetpack' ); ?>">
7184
					<?php esc_html_e( 'Activate Protect', 'jetpack' ); ?>
7185
				</a>
7186
			<?php else : ?>
7187
				<?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?>
7188
			<?php endif; ?>
7189
		</div>
7190
7191
		<div class="akismet">
7192
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
7193
				<h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3>
7194
				<p><?php echo esc_html_x( 'Spam comments blocked by Akismet.', '{#} Spam comments blocked by Akismet -- number is on a prior line, text is a caption.', 'jetpack' ); ?></p>
7195
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
7196
				<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'activate', 'plugin' => 'akismet/akismet.php' ), admin_url( 'plugins.php' ) ), 'activate-plugin_akismet/akismet.php' ) ); ?>" class="button button-jetpack">
7197
					<?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?>
7198
				</a>
7199
			<?php else : ?>
7200
				<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( 'Akismet can help to keep your blog safe from spam!', 'jetpack' ); ?></a></p>
7201
			<?php endif; ?>
7202
		</div>
7203
7204
		</footer>
7205
		<?php
7206
	}
7207
7208
	/**
7209
	 * Return string containing the Jetpack logo.
7210
	 *
7211
	 * @since 3.9.0
7212
	 *
7213
	 * @param bool $logotype Should we use the full logotype (logo + text). Default to false.
7214
	 *
7215
	 * @return string
7216
	 */
7217
	public static function get_jp_emblem( $logotype = false ) {
7218
		$logo = '<path fill="#00BE28" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16c8.8,0,16-7.2,16-16S24.8,0,16,0z M15.2,18.7h-8l8-15.5V18.7z M16.8,28.8 V13.3h8L16.8,28.8z"/>';
7219
		$text = '
7220
<path d="M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z" />
7221
<path d="M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z" />
7222
<path d="M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z" />
7223
<path d="M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z" />
7224
<path d="M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z" />
7225
<path d="M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z" />
7226
<path d="M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z" />
7227
		';
7228
7229
		return sprintf(
7230
			'<svg id="jetpack-logo__icon" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 %1$s 32">%2$s</svg>',
7231
			( true === $logotype ? '118' : '32' ),
7232
			( true === $logotype ? $logo . $text : $logo )
7233
		);
7234
	}
7235
7236
	/*
7237
	 * Adds a "blank" column in the user admin table to display indication of user connection.
7238
	 */
7239
	function jetpack_icon_user_connected( $columns ) {
7240
		$columns['user_jetpack'] = '';
7241
		return $columns;
7242
	}
7243
7244
	/*
7245
	 * Show Jetpack icon if the user is linked.
7246
	 */
7247
	function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
7248
		if ( 'user_jetpack' == $col && Jetpack::is_user_connected( $user_id ) ) {
7249
			$emblem_html = sprintf(
7250
				'<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
7251
				esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
7252
				Jetpack::get_jp_emblem()
7253
			);
7254
			return $emblem_html;
7255
		}
7256
7257
		return $val;
7258
	}
7259
7260
	/*
7261
	 * Style the Jetpack user column
7262
	 */
7263
	function jetpack_user_col_style() {
7264
		global $current_screen;
7265
		if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) { ?>
7266
			<style>
7267
				.fixed .column-user_jetpack {
7268
					width: 21px;
7269
				}
7270
				.jp-emblem-user-admin svg {
7271
					width: 20px;
7272
					height: 20px;
7273
				}
7274
				.jp-emblem-user-admin path {
7275
					fill: #00BE28;
7276
				}
7277
			</style>
7278
		<?php }
7279
	}
7280
7281
	/**
7282
	 * Checks if Akismet is active and working.
7283
	 *
7284
	 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
7285
	 * that implied usage of methods present since more recent version.
7286
	 * See https://github.com/Automattic/jetpack/pull/9585
7287
	 *
7288
	 * @since  5.1.0
7289
	 *
7290
	 * @return bool True = Akismet available. False = Aksimet not available.
7291
	 */
7292
	public static function is_akismet_active() {
7293
		static $status = null;
7294
7295
		if ( ! is_null( $status ) ) {
7296
			return $status;
7297
		}
7298
7299
		// Check if a modern version of Akismet is active.
7300
		if ( ! method_exists( 'Akismet', 'http_post' ) ) {
7301
			$status = false;
7302
			return $status;
7303
		}
7304
7305
		// Make sure there is a key known to Akismet at all before verifying key.
7306
		$akismet_key = Akismet::get_api_key();
7307
		if ( ! $akismet_key ) {
7308
			$status = false;
7309
			return $status;
7310
		}
7311
7312
		// Possible values: valid, invalid, failure via Akismet. false if no status is cached.
7313
		$akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
7314
7315
		// 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.
7316
		$recheck = ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state;
7317
		// We cache the result of the Akismet key verification for ten minutes.
7318
		if ( ! $akismet_key_state || $recheck ) {
7319
			$akismet_key_state = Akismet::verify_key( $akismet_key );
7320
			set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
7321
		}
7322
7323
		$status = 'valid' === $akismet_key_state;
7324
7325
		return $status;
7326
	}
7327
7328
	/**
7329
	 * Checks if one or more function names is in debug_backtrace
7330
	 *
7331
	 * @param $names Mixed string name of function or array of string names of functions
7332
	 *
7333
	 * @return bool
7334
	 */
7335
	public static function is_function_in_backtrace( $names ) {
7336
		$backtrace = debug_backtrace( false ); // phpcs:ignore PHPCompatibility.PHP.NewFunctionParameters.debug_backtrace_optionsFound
7337
		if ( ! is_array( $names ) ) {
7338
			$names = array( $names );
7339
		}
7340
		$names_as_keys = array_flip( $names );
7341
7342
		//Do check in constant O(1) time for PHP5.5+
7343
		if ( function_exists( 'array_column' ) ) {
7344
			$backtrace_functions = array_column( $backtrace, 'function' ); // phpcs:ignore PHPCompatibility.PHP.NewFunctions.array_columnFound
7345
			$backtrace_functions_as_keys = array_flip( $backtrace_functions );
7346
			$intersection = array_intersect_key( $backtrace_functions_as_keys, $names_as_keys );
7347
			return ! empty ( $intersection );
7348
		}
7349
7350
		//Do check in linear O(n) time for < PHP5.5 ( using isset at least prevents O(n^2) )
7351
		foreach ( $backtrace as $call ) {
7352
			if ( isset( $names_as_keys[ $call['function'] ] ) ) {
7353
				return true;
7354
			}
7355
		}
7356
		return false;
7357
	}
7358
7359
	/**
7360
	 * Given a minified path, and a non-minified path, will return
7361
	 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
7362
	 *
7363
	 * Both `$min_base` and `$non_min_base` are expected to be relative to the
7364
	 * root Jetpack directory.
7365
	 *
7366
	 * @since 5.6.0
7367
	 *
7368
	 * @param string $min_path
7369
	 * @param string $non_min_path
7370
	 * @return string The URL to the file
7371
	 */
7372
	public static function get_file_url_for_environment( $min_path, $non_min_path ) {
7373
		$path = ( Jetpack_Constants::is_defined( 'SCRIPT_DEBUG' ) && Jetpack_Constants::get_constant( 'SCRIPT_DEBUG' ) )
7374
			? $non_min_path
7375
			: $min_path;
7376
7377
		return plugins_url( $path, JETPACK__PLUGIN_FILE );
7378
	}
7379
7380
	/**
7381
	 * Checks for whether Jetpack Rewind is enabled.
7382
	 * Will return true if the state of Rewind is anything except "unavailable".
7383
	 * @return bool|int|mixed
7384
	 */
7385
	public static function is_rewind_enabled() {
7386
		if ( ! Jetpack::is_active() ) {
7387
			return false;
7388
		}
7389
7390
		$rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7391
		if ( false === $rewind_enabled ) {
7392
			jetpack_require_lib( 'class.core-rest-api-endpoints' );
7393
			$rewind_data = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7394
			$rewind_enabled = ( ! is_wp_error( $rewind_data )
7395
				&& ! empty( $rewind_data['state'] )
7396
				&& 'active' === $rewind_data['state'] )
7397
				? 1
7398
				: 0;
7399
7400
			set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7401
		}
7402
		return $rewind_enabled;
7403
	}
7404
7405
	/**
7406
	 * Checks whether or not TOS has been agreed upon.
7407
	 * Will return true if a user has clicked to register, or is already connected.
7408
	 */
7409
	public static function jetpack_tos_agreed() {
7410
		return Jetpack_Options::get_option( 'tos_agreed' ) || Jetpack::is_active();
7411
	}
7412
7413
	/**
7414
	 * Handles activating default modules as well general cleanup for the new connection.
7415
	 *
7416
	 * @param boolean $activate_sso                 Whether to activate the SSO module when activating default modules.
7417
	 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7418
	 * @param boolean $send_state_messages          Whether to send state messages.
7419
	 * @return void
7420
	 */
7421
	public static function handle_post_authorization_actions(
7422
		$activate_sso = false,
7423
		$redirect_on_activation_error = false,
7424
		$send_state_messages = true
7425
	) {
7426
		$other_modules = $activate_sso
7427
			? array( 'sso' )
7428
			: array();
7429
7430
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
7431
			Jetpack::delete_active_modules();
7432
7433
			Jetpack::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...
7434
		} else {
7435
			Jetpack::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages );
7436
		}
7437
7438
		// Since this is a fresh connection, be sure to clear out IDC options
7439
		Jetpack_IDC::clear_all_idc_options();
7440
		Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' );
7441
7442
		// Start nonce cleaner
7443
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
7444
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
7445
7446
		if ( $send_state_messages ) {
7447
			Jetpack::state( 'message', 'authorized' );
7448
		}
7449
	}
7450
7451
	/**
7452
	 * Returns a boolean for whether backups UI should be displayed or not.
7453
	 *
7454
	 * @return bool Should backups UI be displayed?
7455
	 */
7456
	public static function show_backups_ui() {
7457
		/**
7458
		 * Whether UI for backups should be displayed.
7459
		 *
7460
		 * @since 6.5.0
7461
		 *
7462
		 * @param bool $show_backups Should UI for backups be displayed? True by default.
7463
		 */
7464
		return Jetpack::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7465
	}
7466
}
7467