Completed
Push — branch-3.8 ( 6dae2a...5ea8e7 )
by
unknown
57:29 queued 49:08
created

Jetpack::sync_theme_data()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 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
class Jetpack {
26
	public $xmlrpc_server = null;
27
28
	private $xmlrpc_verification = null;
29
30
	public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA']
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
31
32
	/**
33
	 * @var array The handles of styles that are concatenated into jetpack.css
34
	 */
35
	public $concatenated_style_handles = array(
36
		'jetpack-carousel',
37
		'grunion.css',
38
		'the-neverending-homepage',
39
		'jetpack_likes',
40
		'jetpack_related-posts',
41
		'sharedaddy',
42
		'jetpack-slideshow',
43
		'presentations',
44
		'jetpack-subscriptions',
45
		'tiled-gallery',
46
		'widget-conditions',
47
		'jetpack_display_posts_widget',
48
		'gravatar-profile-widget',
49
		'widget-grid-and-list',
50
		'jetpack-widgets',
51
		'goodreads-widget',
52
	);
53
54
	public $plugins_to_deactivate = array(
55
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
56
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
57
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
58
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
59
		'after-the-deadline'  => array( 'after-the-deadline/after-the-deadline.php', 'After The Deadline' ),
60
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
61
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
62
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
63
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
64
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
65
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
66
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
67
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
68
		'omnisearch'          => array( 'jetpack-omnisearch/omnisearch.php', 'Jetpack Omnisearch' ),
69
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
70
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' )
71
	);
72
73
	public $capability_translations = array(
74
		'administrator' => 'manage_options',
75
		'editor'        => 'edit_others_posts',
76
		'author'        => 'publish_posts',
77
		'contributor'   => 'edit_posts',
78
		'subscriber'    => 'read',
79
	);
80
81
	/**
82
	 * Map of modules that have conflicts with plugins and should not be auto-activated
83
	 * if the plugins are active.  Used by filter_default_modules
84
	 *
85
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
86
	 * change `module-slug` and add this to your plugin:
87
	 *
88
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
89
	 * function my_jetpack_get_default_modules( $modules ) {
90
	 *     return array_diff( $modules, array( 'module-slug' ) );
91
	 * }
92
	 *
93
	 * @var array
94
	 */
95
	private $conflicting_plugins = array(
96
		'comments'          => array(
97
			'Intense Debate'                    => 'intensedebate/intensedebate.php',
98
			'Disqus'                            => 'disqus-comment-system/disqus.php',
99
			'Livefyre'                          => 'livefyre-comments/livefyre.php',
100
			'Comments Evolved for WordPress'    => 'gplus-comments/comments-evolved.php',
101
			'Google+ Comments'                  => 'google-plus-comments/google-plus-comments.php',
102
			'WP-SpamShield Anti-Spam'           => 'wp-spamshield/wp-spamshield.php',
103
		),
104
		'contact-form'      => array(
105
			'Contact Form 7'                    => 'contact-form-7/wp-contact-form-7.php',
106
			'Gravity Forms'                     => 'gravityforms/gravityforms.php',
107
			'Contact Form Plugin'               => 'contact-form-plugin/contact_form.php',
108
			'Easy Contact Forms'                => 'easy-contact-forms/easy-contact-forms.php',
109
			'Fast Secure Contact Form'          => 'si-contact-form/si-contact-form.php',
110
		),
111
		'minileven'         => array(
112
			'WPtouch'                           => 'wptouch/wptouch.php',
113
		),
114
		'latex'             => array(
115
			'LaTeX for WordPress'               => 'latex/latex.php',
116
			'Youngwhans Simple Latex'           => 'youngwhans-simple-latex/yw-latex.php',
117
			'Easy WP LaTeX'                     => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
118
			'MathJax-LaTeX'                     => 'mathjax-latex/mathjax-latex.php',
119
			'Enable Latex'                      => 'enable-latex/enable-latex.php',
120
			'WP QuickLaTeX'                     => 'wp-quicklatex/wp-quicklatex.php',
121
		),
122
		'protect'           => array(
123
			'Limit Login Attempts'              => 'limit-login-attempts/limit-login-attempts.php',
124
			'Captcha'                           => 'captcha/captcha.php',
125
			'Brute Force Login Protection'      => 'brute-force-login-protection/brute-force-login-protection.php',
126
			'Login Security Solution'           => 'login-security-solution/login-security-solution.php',
127
			'WPSecureOps Brute Force Protect'   => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
128
			'BulletProof Security'              => 'bulletproof-security/bulletproof-security.php',
129
			'SiteGuard WP Plugin'               => 'siteguard/siteguard.php',
130
			'Security-protection'               => 'security-protection/security-protection.php',
131
			'Login Security'                    => 'login-security/login-security.php',
132
			'Botnet Attack Blocker'             => 'botnet-attack-blocker/botnet-attack-blocker.php',
133
			'Wordfence Security'                => 'wordfence/wordfence.php',
134
			'All In One WP Security & Firewall' => 'all-in-one-wp-security-and-firewall/wp-security.php',
135
			'iThemes Security'                  => 'better-wp-security/better-wp-security.php',
136
		),
137
		'random-redirect'   => array(
138
			'Random Redirect 2'                 => 'random-redirect-2/random-redirect.php',
139
		),
140
		'related-posts'     => array(
141
			'YARPP'                             => 'yet-another-related-posts-plugin/yarpp.php',
142
			'WordPress Related Posts'           => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
143
			'nrelate Related Content'           => 'nrelate-related-content/nrelate-related.php',
144
			'Contextual Related Posts'          => 'contextual-related-posts/contextual-related-posts.php',
145
			'Related Posts for WordPress'       => 'microkids-related-posts/microkids-related-posts.php',
146
			'outbrain'                          => 'outbrain/outbrain.php',
147
			'Shareaholic'                       => 'shareaholic/shareaholic.php',
148
			'Sexybookmarks'                     => 'sexybookmarks/shareaholic.php',
149
		),
150
		'sharedaddy'        => array(
151
			'AddThis'                           => 'addthis/addthis_social_widget.php',
152
			'Add To Any'                        => 'add-to-any/add-to-any.php',
153
			'ShareThis'                         => 'share-this/sharethis.php',
154
			'Shareaholic'                       => 'shareaholic/shareaholic.php',
155
		),
156
		'verification-tools' => array(
157
			'WordPress SEO by Yoast'            => 'wordpress-seo/wp-seo.php',
158
			'WordPress SEO Premium by Yoast'    => 'wordpress-seo-premium/wp-seo-premium.php',
159
			'All in One SEO Pack'               => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
160
		),
161
		'widget-visibility' => array(
162
			'Widget Logic'                      => 'widget-logic/widget_logic.php',
163
			'Dynamic Widgets'                   => 'dynamic-widgets/dynamic-widgets.php',
164
		),
165
	);
166
167
	/**
168
	 * Plugins for which we turn off our Facebook OG Tags implementation.
169
	 *
170
	 * Note: WordPress SEO by Yoast and WordPress SEO Premium by Yoast automatically deactivate
171
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
172
	 *
173
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
174
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
175
	 */
176
	private $open_graph_conflicting_plugins = array(
177
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
178
		                                                         // 2 Click Social Media Buttons
179
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
180
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
181
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
182
		'facebook/facebook.php',                                 // Facebook (official plugin)
183
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
184
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
185
		                                                         // Facebook Featured Image & OG Meta Tags
186
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
187
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
188
		                                                         // Facebook Open Graph Meta Tags for WordPress
189
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
190
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
191
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
192
		                                                         // Fedmich's Facebook Open Graph Meta
193
		'header-footer/plugin.php',                              // Header and Footer
194
		'network-publisher/networkpub.php',                      // Network Publisher
195
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
196
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
197
		                                                         // NextScripts SNAP
198
		'opengraph/opengraph.php',                               // Open Graph
199
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
200
		                                                         // Open Graph Protocol Framework
201
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
202
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
203
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
204
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
205
		'sharepress/sharepress.php',                             // SharePress
206
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
207
		'social-discussions/social-discussions.php',             // Social Discussions
208
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
209
		'socialize/socialize.php',                               // Socialize
210
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
211
		                                                         // Tweet, Like, Google +1 and Share
212
		'wordbooker/wordbooker.php',                             // Wordbooker
213
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
214
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
215
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
216
		                                                         // WP Facebook Like Send & Open Graph Meta
217
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
218
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
219
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
220
		'wp-fb-share-like-button/wp_fb_share-like_widget.php'    // WP Facebook Like Button
221
	);
222
223
	/**
224
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
225
	 */
226
	private $twitter_cards_conflicting_plugins = array(
227
	//	'twitter/twitter.php',                       // The official one handles this on its own.
228
	//	                                             // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
229
		'eewee-twitter-card/index.php',              // Eewee Twitter Card
230
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
231
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
232
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
233
		                                             // Pure Web Brilliant's Social Graph Twitter Cards Extension
234
		'twitter-cards/twitter-cards.php',           // Twitter Cards
235
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
236
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
237
	);
238
239
	/**
240
	 * Message to display in admin_notice
241
	 * @var string
242
	 */
243
	public $message = '';
244
245
	/**
246
	 * Error to display in admin_notice
247
	 * @var string
248
	 */
249
	public $error = '';
250
251
	/**
252
	 * Modules that need more privacy description.
253
	 * @var string
254
	 */
255
	public $privacy_checks = '';
256
257
	/**
258
	 * Stats to record once the page loads
259
	 *
260
	 * @var array
261
	 */
262
	public $stats = array();
263
264
	/**
265
	 * Allows us to build a temporary security report
266
	 *
267
	 * @var array
268
	 */
269
	static $security_report = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $security_report.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
270
271
	/**
272
	 * Jetpack_Sync object
273
	 */
274
	public $sync;
275
276
	/**
277
	 * Verified data for JSON authorization request
278
	 */
279
	public $json_api_authorization_request = array();
280
281
	/**
282
	 * Holds the singleton instance of this class
283
	 * @since 2.3.3
284
	 * @var Jetpack
285
	 */
286
	static $instance = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $instance.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
287
288
	/**
289
	 * Singleton
290
	 * @static
291
	 */
292
	public static function init() {
293
		if ( ! self::$instance ) {
294
			if ( did_action( 'plugins_loaded' ) )
295
				self::plugin_textdomain();
296
			else
297
				add_action( 'plugins_loaded', array( __CLASS__, 'plugin_textdomain' ), 99 );
298
299
			self::$instance = new Jetpack;
300
301
			self::$instance->plugin_upgrade();
302
303
			add_action( 'init', array( __CLASS__, 'perform_security_reporting' ) );
304
305
		}
306
307
		return self::$instance;
308
	}
309
310
	/**
311
	 * Must never be called statically
312
	 */
313
	function plugin_upgrade() {
314
		// Upgrade: 1.1 -> 1.2
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
315
		if ( get_option( 'jetpack_id' ) ) {
316
			// Move individual jetpack options to single array of options
317
			$options = array();
318
			foreach ( Jetpack_Options::get_option_names() as $option ) {
319
				if ( false !== $value = get_option( "jetpack_$option" ) ) {
320
					$options[$option] = $value;
321
				}
322
			}
323
324
			if ( $options ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options 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...
325
				Jetpack_Options::update_options( $options );
326
327
				foreach ( array_keys( $options ) as $option ) {
328
					delete_option( "jetpack_$option" );
329
				}
330
			}
331
332
			// Add missing version and old_version options
333 View Code Duplication
			if ( ! $version = Jetpack_Options::get_option( 'version' ) ) {
334
				$version = $old_version = '1.1:' . time();
335
				/**
336
				 * Fires on update, before bumping version numbers up to a new version.
337
				 *
338
				 * @since 3.4.0
339
				 *
340
				 * @param string $version Jetpack version number.
341
				 * @param bool false Does an old version exist. Default is false.
342
				 */
343
				do_action( 'updating_jetpack_version', $version, false );
344
				Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
345
			}
346
		}
347
348
		// Upgrade from a single user token to a user_id-indexed array and a master_user ID
349
		if ( ! Jetpack_Options::get_option( 'user_tokens' ) ) {
350
			if ( $user_token = Jetpack_Options::get_option( 'user_token' ) ) {
351
				$token_parts = explode( '.', $user_token );
352
				if ( isset( $token_parts[2] ) ) {
353
					$master_user = $token_parts[2];
354
					$user_tokens = array( $master_user => $user_token );
355
					Jetpack_Options::update_options( compact( 'master_user', 'user_tokens' ) );
356
					Jetpack_Options::delete_option( 'user_token' );
357
				} else {
358
					// @todo: is this even possible?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
359
					trigger_error( sprintf( 'Jetpack::plugin_upgrade found no user_id in user_token "%s"', $user_token ), E_USER_WARNING );
360
				}
361
			}
362
		}
363
364
		// Clean up legacy G+ Authorship data.
365
		if ( get_option( 'gplus_authors' ) ) {
366
			delete_option( 'gplus_authors' );
367
			delete_option( 'hide_gplus' );
368
			delete_metadata( 'post', 0, 'gplus_authorship_disabled', null, true );
369
		}
370
371
		if ( ! get_option( 'jetpack_private_options' ) ) {
372
			$jetpack_options = get_option( 'jetpack_options', array() );
373
			foreach( Jetpack_Options::get_option_names( 'private' ) as $option_name ) {
374
				if ( isset( $jetpack_options[ $option_name ] ) ) {
375
					Jetpack_Options::update_option( $option_name, $jetpack_options[ $option_name ] );
376
					unset( $jetpack_options[ $option_name ] );
377
				}
378
			}
379
			update_option( 'jetpack_options', $jetpack_options );
380
		}
381
382
		if ( Jetpack::is_active() ) {
383
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
384
			if ( JETPACK__VERSION != $version ) {
385
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
386
				/**
387
				 * Fires when synchronizing all registered options and constants.
388
				 *
389
				 * @since 3.3.0
390
				 */
391
				do_action( 'jetpack_sync_all_registered_options' );
392
			}
393
394
			//if Jetpack is connected check if jetpack_unique_connection exists and if not then set it
395
			$jetpack_unique_connection = get_option( 'jetpack_unique_connection' );
396
			$is_unique_connection = $jetpack_unique_connection && array_key_exists( 'version', $jetpack_unique_connection );
397
			if ( ! $is_unique_connection ) {
398
				$jetpack_unique_connection = array(
399
					'connected'     => 1,
400
					'disconnected'  => -1,
401
					'version'       => '3.6.1'
402
				);
403
				update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
404
			}
405
		}
406
407
		if ( get_option( 'jetpack_json_api_full_management' ) ) {
408
			delete_option( 'jetpack_json_api_full_management' );
409
			self::activate_manage();
410
		}
411
412
	}
413
414
	static function activate_manage( ) {
415
416
		if ( did_action( 'init' ) || current_filter() == 'init' ) {
417
			self::activate_module( 'manage', false, false );
418
		} else if ( !  has_action( 'init' , array( __CLASS__, 'activate_manage' ) ) ) {
419
			add_action( 'init', array( __CLASS__, 'activate_manage' ) );
420
		}
421
422
	}
423
424
	/**
425
	 * Constructor.  Initializes WordPress hooks
426
	 */
427
	private function __construct() {
428
		/*
429
		 * Check for and alert any deprecated hooks
430
		 */
431
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
432
433
		/*
434
		 * Do things that should run even in the network admin
435
		 * here, before we potentially fail out.
436
		 */
437
		add_filter( 'jetpack_require_lib_dir', array( $this, 'require_lib_dir' ) );
438
439
		/**
440
		 * We need sync object even in Multisite mode
441
		 */
442
		$this->sync = new Jetpack_Sync;
443
444
		/**
445
		 * Trigger a wp_version sync when updating WP versions
446
		 **/
447
		add_action( 'upgrader_process_complete', array( 'Jetpack', 'update_get_wp_version' ), 10, 2 );
448
		$this->sync->mock_option( 'wp_version', array( 'Jetpack', 'get_wp_version' ) );
449
450
		add_action( 'init', array( $this, 'sync_update_data') );
451
		add_action( 'init', array( $this, 'sync_theme_data' ) );
452
453
		/*
454
		 * Load things that should only be in Network Admin.
455
		 *
456
		 * For now blow away everything else until a more full
457
		 * understanding of what is needed at the network level is
458
		 * available
459
		 */
460
		if( is_multisite() ) {
461
			Jetpack_Network::init();
462
463
			// Only sync this info if we are on a multi site
464
			// @since  3.7
465
			$this->sync->mock_option( 'network_name', array( 'Jetpack', 'network_name' ) );
466
			$this->sync->mock_option( 'network_allow_new_registrations', array( 'Jetpack', 'network_allow_new_registrations' ) );
467
			$this->sync->mock_option( 'network_add_new_users', array( 'Jetpack', 'network_add_new_users' ) );
468
			$this->sync->mock_option( 'network_site_upload_space', array( 'Jetpack', 'network_site_upload_space' ) );
469
			$this->sync->mock_option( 'network_upload_file_types', array( 'Jetpack', 'network_upload_file_types' ) );
470
			$this->sync->mock_option( 'network_enable_administration_menus', array( 'Jetpack', 'network_enable_administration_menus' ) );
471
472
			if( is_network_admin() ) {
473
				// Sync network site data if it is updated or not.
474
				add_action( 'update_wpmu_options', array( $this, 'update_jetpack_network_settings' ) );
475
				return; // End here to prevent single site actions from firing
476
			}
477
		}
478
479
480
		$theme_slug = get_option( 'stylesheet' );
481
482
483
		// Modules should do Jetpack_Sync::sync_options( __FILE__, $option, ... ); instead
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
484
		// We access the "internal" method here only because the Jetpack object isn't instantiated yet
485
		$this->sync->options(
486
			JETPACK__PLUGIN_DIR . 'jetpack.php',
487
			'home',
488
			'siteurl',
489
			'blogname',
490
			'gmt_offset',
491
			'timezone_string',
492
			'security_report',
493
			'stylesheet',
494
			"theme_mods_{$theme_slug}",
495
			'jetpack_sync_non_public_post_stati',
496
			'jetpack_options',
497
			'site_icon', // (int) - ID of core's Site Icon attachment ID
498
			'default_post_format',
499
			'default_category',
500
			'large_size_w',
501
			'large_size_h',
502
			'thumbnail_size_w',
503
			'thumbnail_size_h',
504
			'medium_size_w',
505
			'medium_size_h',
506
			'thumbnail_crop',
507
			'image_default_link_type'
508
		);
509
510
		foreach( Jetpack_Options::get_option_names( 'non-compact' ) as $option ) {
511
			$this->sync->options( __FILE__, 'jetpack_' . $option );
512
		}
513
514
		/**
515
		 * Sometimes you want to sync data to .com without adding options to .org sites.
516
		 * The mock option allows you to do just that.
517
		 */
518
		$this->sync->mock_option( 'is_main_network',   array( $this, 'is_main_network_option' ) );
519
		$this->sync->mock_option( 'is_multi_site', array( $this, 'is_multisite' ) );
520
		$this->sync->mock_option( 'main_network_site', array( $this, 'jetpack_main_network_site_option' ) );
521
		$this->sync->mock_option( 'single_user_site', array( 'Jetpack', 'is_single_user_site' ) );
522
		$this->sync->mock_option( 'stat_data', array( $this, 'get_stat_data' ) );
523
524
		$this->sync->mock_option( 'has_file_system_write_access', array( 'Jetpack', 'file_system_write_access' ) );
525
		$this->sync->mock_option( 'is_version_controlled', array( 'Jetpack', 'is_version_controlled' ) );
526
		$this->sync->mock_option( 'max_upload_size', 'wp_max_upload_size' );
527
		$this->sync->mock_option( 'content_width', array( 'Jetpack', 'get_content_width' ) );
528
529
		/**
530
		 * Trigger an update to the main_network_site when we update the blogname of a site.
531
		 *
532
		 */
533
		add_action( 'update_option_siteurl', array( $this, 'update_jetpack_main_network_site_option' ) );
534
535
		add_action( 'update_option', array( $this, 'log_settings_change' ), 10, 3 );
536
537
		// Update the settings everytime the we register a new user to the site or we delete a user.
538
		add_action( 'user_register', array( $this, 'is_single_user_site_invalidate' ) );
539
		add_action( 'deleted_user', array( $this, 'is_single_user_site_invalidate' ) );
540
541
		// Unlink user before deleting the user from .com
542
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
543
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
544
545
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
546
			@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...
547
548
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
549
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
550
551
			$this->require_jetpack_authentication();
552
553
			if ( Jetpack::is_active() ) {
554
				// Hack to preserve $HTTP_RAW_POST_DATA
555
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
556
557
				$signed = $this->verify_xml_rpc_signature();
558
				if ( $signed && ! is_wp_error( $signed ) ) {
559
					// The actual API methods.
560
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
561
				} else {
562
					add_filter( 'xmlrpc_methods', '__return_empty_array' );
563
				}
564
			} else {
565
				// The bootstrap API methods.
566
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
567
			}
568
569
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
570
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
571
		} elseif ( is_admin() && isset( $_POST['action'] ) && 'jetpack_upload_file' == $_POST['action'] ) {
572
			$this->require_jetpack_authentication();
573
			$this->add_remote_request_handlers();
574
		} else {
575
			if ( Jetpack::is_active() ) {
576
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
577
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
578
			}
579
		}
580
581
		if ( Jetpack::is_active() ) {
582
			Jetpack_Heartbeat::init();
583
		}
584
585
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
586
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
587
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
588
		}
589
590
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
591
592
		add_action( 'admin_init', array( $this, 'admin_init' ) );
593
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
594
595
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
596
597
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
598
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
599
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
600
601
		add_action( 'wp_ajax_jetpack-sync-reindex-trigger', array( $this, 'sync_reindex_trigger' ) );
602
		add_action( 'wp_ajax_jetpack-sync-reindex-status', array( $this, 'sync_reindex_status' ) );
603
604
		// Jump Start AJAX callback function
605
		add_action( 'wp_ajax_jetpack_jumpstart_ajax',  array( $this, 'jetpack_jumpstart_ajax_callback' ) );
606
		add_action( 'update_option', array( $this, 'jumpstart_has_updated_module_option' ) );
607
608
		// Identity Crisis AJAX callback function
609
		add_action( 'wp_ajax_jetpack_resolve_identity_crisis', array( $this, 'resolve_identity_crisis_ajax_callback' ) );
610
611
		// JITM AJAX callback function
612
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
613
614
		add_action( 'wp_ajax_jetpack_admin_ajax',          array( $this, 'jetpack_admin_ajax_callback' ) );
615
		add_action( 'wp_ajax_jetpack_admin_ajax_refresh',  array( $this, 'jetpack_admin_ajax_refresh_data' ) );
616
617
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
618
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
619
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
620
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
621
622
		add_action( 'jetpack_activate_module', array( $this, 'activate_module_actions' ) );
623
624
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
625
626
		add_action( 'jetpack_notices', array( $this, 'show_development_mode_notice' ) );
627
628
		/**
629
		 * These actions run checks to load additional files.
630
		 * They check for external files or plugins, so they need to run as late as possible.
631
		 */
632
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
633
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
634
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
635
636
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
637
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
638
639
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
640
641
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
642
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
643
644
		// A filter to control all just in time messages
645
		add_filter( 'jetpack_just_in_time_msgs', '__return_true' );
646
647
		/**
648
		 * This is the hack to concatinate all css files into one.
649
		 * For description and reasoning see the implode_frontend_css method
650
		 *
651
		 * Super late priority so we catch all the registered styles
652
		 */
653
		if( !is_admin() ) {
654
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
655
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
656
		}
657
658
		// Sync Core Icon: Detect changes in Core's Site Icon and make it syncable.
659
		add_action( 'add_option_site_icon',    array( $this, 'jetpack_sync_core_icon' ) );
660
		add_action( 'update_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
661
		add_action( 'delete_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
662
		add_action( 'jetpack_heartbeat',       array( $this, 'jetpack_sync_core_icon' ) );
663
664
	}
665
666
	/*
667
	 * Make sure any site icon added to core can get
668
	 * synced back to dotcom, so we can display it there.
669
	 */
670
	function jetpack_sync_core_icon() {
671
		if ( function_exists( 'get_site_icon_url' ) ) {
672
			$url = get_site_icon_url();
673
		} else {
674
			return;
675
		}
676
677
		require_once( JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php' );
678
		// If there's a core icon, maybe update the option.  If not, fall back to Jetpack's.
679
		if ( ! empty( $url ) && $url !== jetpack_site_icon_url() ) {
680
			// This is the option that is synced with dotcom
681
			Jetpack_Options::update_option( 'site_icon_url', $url );
682
		} else if ( empty( $url ) && did_action( 'delete_option_site_icon' ) ) {
683
			Jetpack_Options::delete_option( 'site_icon_url' );
684
		}
685
	}
686
687
	function jetpack_admin_ajax_callback() {
688
		// Check for nonce
689 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) || ! current_user_can( 'jetpack_manage_modules' ) ) {
690
			wp_die( 'permissions check failed' );
691
		}
692
693
		if ( isset( $_REQUEST['toggleModule'] ) && 'nux-toggle-module' == $_REQUEST['toggleModule'] ) {
694
			$slug = $_REQUEST['thisModuleSlug'];
695
696
			if ( ! in_array( $slug, Jetpack::get_available_modules() ) ) {
697
				wp_die( 'That is not a Jetpack module slug' );
698
			}
699
700
			if ( Jetpack::is_module_active( $slug ) ) {
701
				Jetpack::deactivate_module( $slug );
702
			} else {
703
				Jetpack::activate_module( $slug, false, false );
704
			}
705
706
			$modules = Jetpack_Admin::init()->get_modules();
707
			echo json_encode( $modules[ $slug ] );
708
709
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method jetpack_admin_ajax_callback() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
710
		}
711
712
		wp_die();
713
	}
714
715
	/*
716
	 * Sometimes we need to refresh the data,
717
	 * especially if the page is visited via a 'history'
718
	 * event like back/forward
719
	 */
720
	function jetpack_admin_ajax_refresh_data() {
721
		// Check for nonce
722 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) ) {
723
			wp_die( 'permissions check failed' );
724
		}
725
726
		if ( isset( $_REQUEST['refreshData'] ) && 'refresh' == $_REQUEST['refreshData'] ) {
727
			$modules = Jetpack_Admin::init()->get_modules();
728
			echo json_encode( $modules );
729
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method jetpack_admin_ajax_refresh_data() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
730
		}
731
732
		wp_die();
733
	}
734
735
	/**
736
	 * The callback for the Jump Start ajax requests.
737
	 */
738
	function jetpack_jumpstart_ajax_callback() {
739
		// Check for nonce
740
		if ( ! isset( $_REQUEST['jumpstartNonce'] ) || ! wp_verify_nonce( $_REQUEST['jumpstartNonce'], 'jetpack-jumpstart-nonce' ) )
741
			wp_die( 'permissions check failed' );
742
743
		if ( isset( $_REQUEST['jumpStartActivate'] ) && 'jump-start-activate' == $_REQUEST['jumpStartActivate'] ) {
744
			// Update the jumpstart option
745
			if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
746
				Jetpack_Options::update_option( 'jumpstart', 'jumpstart_activated' );
747
			}
748
749
			// Loops through the requested "Jump Start" modules, and activates them.
750
			// Custom 'no_message' state, so that no message will be shown on reload.
751
			$modules = $_REQUEST['jumpstartModSlug'];
752
			$module_slugs = array();
753
			foreach( $modules as $module => $value ) {
754
				$module_slugs[] = $value['module_slug'];
755
			}
756
757
			// Check for possible conflicting plugins
758
			$module_slugs_filtered = $this->filter_default_modules( $module_slugs );
759
760
			foreach ( $module_slugs_filtered as $module_slug ) {
761
				Jetpack::log( 'activate', $module_slug );
762
				Jetpack::activate_module( $module_slug, false, false );
763
				Jetpack::state( 'message', 'no_message' );
764
			}
765
766
			// Set the default sharing buttons and set to display on posts if none have been set.
767
			$sharing_services = get_option( 'sharing-services' );
768
			$sharing_options  = get_option( 'sharing-options' );
769
			if ( empty( $sharing_services['visible'] ) ) {
770
				// Default buttons to set
771
				$visible = array(
772
					'twitter',
773
					'facebook',
774
					'google-plus-1',
775
				);
776
				$hidden = array();
777
778
				// Set some sharing settings
779
				$sharing = new Sharing_Service();
780
				$sharing_options['global'] = array(
781
					'button_style'  => 'icon',
782
					'sharing_label' => $sharing->default_sharing_label,
783
					'open_links'    => 'same',
784
					'show'          => array( 'post' ),
785
					'custom'        => isset( $sharing_options['global']['custom'] ) ? $sharing_options['global']['custom'] : array()
786
				);
787
788
				update_option( 'sharing-options', $sharing_options );
789
790
				// Send a success response so that we can display an error message.
791
				$success = update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) );
792
				echo json_encode( $success );
793
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method jetpack_jumpstart_ajax_callback() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
794
			}
795
796
		} elseif ( isset( $_REQUEST['disableJumpStart'] ) && true == $_REQUEST['disableJumpStart'] ) {
797
			// If dismissed, flag the jumpstart option as such.
798
			// Send a success response so that we can display an error message.
799
			if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
800
				$success = Jetpack_Options::update_option( 'jumpstart', 'jumpstart_dismissed' );
801
				echo json_encode( $success );
802
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method jetpack_jumpstart_ajax_callback() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
803
			}
804
805
		} elseif ( isset( $_REQUEST['jumpStartDeactivate'] ) && 'jump-start-deactivate' == $_REQUEST['jumpStartDeactivate'] ) {
806
807
			// FOR TESTING ONLY
808
			// @todo remove
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
809
			$modules = (array) $_REQUEST['jumpstartModSlug'];
810
			foreach( $modules as $module => $value ) {
811
				if ( !in_array( $value['module_slug'], Jetpack::get_default_modules() ) ) {
812
					Jetpack::log( 'deactivate', $value['module_slug'] );
813
					Jetpack::deactivate_module( $value['module_slug'] );
814
					Jetpack::state( 'message', 'no_message' );
815
				} else {
816
					Jetpack::log( 'activate', $value['module_slug'] );
817
					Jetpack::activate_module( $value['module_slug'], false, false );
818
					Jetpack::state( 'message', 'no_message' );
819
				}
820
			}
821
822
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
823
			echo "reload the page";
824
		}
825
826
		wp_die();
827
	}
828
829
	/**
830
	 * The callback for the JITM ajax requests.
831
	 */
832
	function jetpack_jitm_ajax_callback() {
833
		// Check for nonce
834
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
835
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
836
		}
837
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
838
			$module_slug = $_REQUEST['jitmModule'];
839
			Jetpack::log( 'activate', $module_slug );
840
			Jetpack::activate_module( $module_slug, false, false );
841
			Jetpack::state( 'message', 'no_message' );
842
843
			//A Jetpack module is being activated through a JITM, track it
844
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
845
			$this->do_stats( 'server_side' );
846
847
			wp_send_json_success();
848
		}
849
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
850
			// get the hide_jitm options array
851
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
852
			$module_slug = $_REQUEST['jitmModule'];
853
854
			if( ! $jetpack_hide_jitm ) {
855
				$jetpack_hide_jitm = array(
856
					$module_slug => 'hide'
857
				);
858
			} else {
859
				$jetpack_hide_jitm[$module_slug] = 'hide';
860
			}
861
862
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
863
864
			//jitm is being dismissed forever, track it
865
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
866
			$this->do_stats( 'server_side' );
867
868
			wp_send_json_success();
869
		}
870
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
871
			$module_slug = $_REQUEST['jitmModule'];
872
873
			// User went to WordPress.com, track this
874
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
875
			$this->do_stats( 'server_side' );
876
877
			wp_send_json_success();
878
		}
879
	}
880
881
	/**
882
	 * If there are any stats that need to be pushed, but haven't been, push them now.
883
	 */
884
	function __destruct() {
885
		if ( ! empty( $this->stats ) ) {
886
			$this->do_stats( 'server_side' );
887
		}
888
	}
889
890
	function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $user_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
891
		switch( $cap ) {
892
			case 'jetpack_connect' :
893
			case 'jetpack_reconnect' :
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
894
				if ( Jetpack::is_development_mode() ) {
895
					$caps = array( 'do_not_allow' );
896
					break;
897
				}
898
				/**
899
				 * Pass through. If it's not development mode, these should match disconnect.
900
				 * Let users disconnect if it's development mode, just in case things glitch.
901
				 */
902
			case 'jetpack_disconnect' :
903
				/**
904
				 * In multisite, can individual site admins manage their own connection?
905
				 *
906
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
907
				 */
908
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
909
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
910
						/**
911
						 * We need to update the option name -- it's terribly unclear which
912
						 * direction the override goes.
913
						 *
914
						 * @todo: Update the option name to `sub-sites-can-manage-own-connections`
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
915
						 */
916
						$caps = array( 'do_not_allow' );
917
						break;
918
					}
919
				}
920
921
				$caps = array( 'manage_options' );
922
				break;
923
			case 'jetpack_manage_modules' :
924
			case 'jetpack_activate_modules' :
925
			case 'jetpack_deactivate_modules' :
926
				$caps = array( 'manage_options' );
927
				break;
928
			case 'jetpack_configure_modules' :
929
				$caps = array( 'manage_options' );
930
				break;
931
			case 'jetpack_network_admin_page':
932
			case 'jetpack_network_settings_page':
933
				$caps = array( 'manage_network_plugins' );
934
				break;
935
			case 'jetpack_network_sites_page':
936
				$caps = array( 'manage_sites' );
937
				break;
938
			case 'jetpack_admin_page' :
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
939
				if ( Jetpack::is_development_mode() ) {
940
					$caps = array( 'manage_options' );
941
					break;
942
				}
943
944
				// Don't ever show to subscribers, but allow access to the page if they're trying to unlink.
945
				if ( ! current_user_can( 'edit_posts' ) ) {
946
					if ( isset( $_GET['redirect'] ) && 'sub-unlink' == $_GET['redirect'] ) {
947
						// We need this in order to unlink the user.
948
						$this->admin_page_load();
949
					}
950
					if ( ! wp_verify_nonce( 'jetpack-unlink' ) ) {
951
						$caps = array( 'do_not_allow' );
952
						break;
953
					}
954
				}
955
956
				if ( ! self::is_active() && ! current_user_can( 'jetpack_connect' ) ) {
957
					$caps = array( 'do_not_allow' );
958
					break;
959
				}
960
				/**
961
				 * Pass through. If it's not development mode, these should match the admin page.
962
				 * Let users disconnect if it's development mode, just in case things glitch.
963
				 */
964
			case 'jetpack_connect_user' :
965
				if ( Jetpack::is_development_mode() ) {
966
					$caps = array( 'do_not_allow' );
967
					break;
968
				}
969
				$caps = array( 'read' );
970
				break;
971
		}
972
		return $caps;
973
	}
974
975
	function require_jetpack_authentication() {
976
		// Don't let anyone authenticate
977
		$_COOKIE = array();
978
		remove_all_filters( 'authenticate' );
979
980
		/**
981
		 * For the moment, remove Limit Login Attempts if its xmlrpc for Jetpack.
982
		 * If Limit Login Attempts is installed as a mu-plugin, it can occasionally
983
		 * generate false-positives.
984
		 */
985
		remove_filter( 'wp_login_failed', 'limit_login_failed' );
986
987
		if ( Jetpack::is_active() ) {
988
			// Allow Jetpack authentication
989
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
990
		}
991
	}
992
993
	/**
994
	 * Load language files
995
	 */
996
	public static function plugin_textdomain() {
997
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
998
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
999
	}
1000
1001
	/**
1002
	 * Register assets for use in various modules and the Jetpack admin page.
1003
	 *
1004
	 * @uses wp_script_is, wp_register_script, plugins_url
1005
	 * @action wp_loaded
1006
	 * @return null
1007
	 */
1008
	public function register_assets() {
1009
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
1010
			wp_register_script( 'spin', plugins_url( '_inc/spin.js', JETPACK__PLUGIN_FILE ), false, '1.3' );
1011
		}
1012
1013
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
1014
			wp_register_script( 'jquery.spin', plugins_url( '_inc/jquery.spin.js', JETPACK__PLUGIN_FILE ) , array( 'jquery', 'spin' ), '1.3' );
1015
		}
1016
1017 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1018
			wp_register_script( 'jetpack-gallery-settings', plugins_url( '_inc/gallery-settings.js', JETPACK__PLUGIN_FILE ), array( 'media-views' ), '20121225' );
1019
		}
1020
1021
		/**
1022
		 * As jetpack_register_genericons is by default fired off a hook,
1023
		 * the hook may have already fired by this point.
1024
		 * So, let's just trigger it manually.
1025
		 */
1026
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
1027
		jetpack_register_genericons();
1028
1029 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
1030
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1031
	}
1032
1033
	/**
1034
	 * Device Pixels support
1035
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
1036
	 */
1037
	function devicepx() {
1038
		if ( Jetpack::is_active() ) {
1039
			wp_enqueue_script( 'devicepx', set_url_scheme( 'http://s0.wp.com/wp-content/js/devicepx-jetpack.js' ), array(), gmdate( 'oW' ), true );
1040
		}
1041
	}
1042
1043
	/*
1044
	 * Returns the location of Jetpack's lib directory. This filter is applied
1045
	 * in require_lib().
1046
	 *
1047
	 * @filter require_lib_dir
1048
	 */
1049
	function require_lib_dir() {
1050
		return JETPACK__PLUGIN_DIR . '_inc/lib';
1051
	}
1052
1053
	/**
1054
	 * Return the network_site_url so that .com knows what network this site is a part of.
1055
	 * @param  bool $option
1056
	 * @return string
1057
	 */
1058
	public function jetpack_main_network_site_option( $option ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1059
		return network_site_url();
1060
	}
1061
	/**
1062
	 * Network Name.
1063
	 */
1064
	static function network_name( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1065
		global $current_site;
1066
		return $current_site->site_name;
1067
	}
1068
	/**
1069
	 * Does the network allow new user and site registrations.
1070
	 * @return string
1071
	 */
1072
	static function network_allow_new_registrations( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1073
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
1074
	}
1075
	/**
1076
	 * Does the network allow admins to add new users.
1077
	 * @return boolian
1078
	 */
1079
	static function network_add_new_users( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1080
		return (bool) get_site_option( 'add_new_users' );
1081
	}
1082
	/**
1083
	 * File upload psace left per site in MB.
1084
	 *  -1 means NO LIMIT.
1085
	 * @return number
1086
	 */
1087
	static function network_site_upload_space( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1088
		// value in MB
1089
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1090
	}
1091
1092
	/**
1093
	 * Network allowed file types.
1094
	 * @return string
1095
	 */
1096
	static function network_upload_file_types( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1097
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1098
	}
1099
1100
	/**
1101
	 * Maximum file upload size set by the network.
1102
	 * @return number
1103
	 */
1104
	static function network_max_upload_file_size( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1105
		// value in KB
1106
		return get_site_option( 'fileupload_maxk', 300 );
1107
	}
1108
1109
	/**
1110
	 * Lets us know if a site allows admins to manage the network.
1111
	 * @return array
1112
	 */
1113
	static function network_enable_administration_menus( $option = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1114
		return get_site_option( 'menu_items' );
1115
	}
1116
1117
	/**
1118
	 * Return whether we are dealing with a multi network setup or not.
1119
	 * The reason we are type casting this is because we want to avoid the situation where
1120
	 * the result is false since when is_main_network_option return false it cases
1121
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1122
	 * database which could be set to anything as opposed to what this function returns.
1123
	 * @param  bool  $option
1124
	 *
1125
	 * @return boolean
1126
	 */
1127
	public function is_main_network_option( $option ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1128
		// return '1' or ''
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1129
		return (string) (bool) Jetpack::is_multi_network();
1130
	}
1131
1132
	/**
1133
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1134
	 *
1135
	 * @param  string  $option
1136
	 * @return boolean
1137
	 */
1138
	public function is_multisite( $option ) {
0 ignored issues
show
Unused Code introduced by
The parameter $option is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1139
		return (string) (bool) is_multisite();
1140
	}
1141
1142
	/**
1143
	 * Implemented since there is no core is multi network function
1144
	 * Right now there is no way to tell if we which network is the dominant network on the system
1145
	 *
1146
	 * @since  3.3
1147
	 * @return boolean
1148
	 */
1149
	public static function is_multi_network() {
1150
		global  $wpdb;
1151
1152
		// if we don't have a multi site setup no need to do any more
1153
		if ( ! is_multisite() ) {
1154
			return false;
1155
		}
1156
1157
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1158
		if ( $num_sites > 1 ) {
1159
			return true;
1160
		} else {
1161
			return false;
1162
		}
1163
	}
1164
1165
	/**
1166
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1167
	 * @return null
1168
	 */
1169
	function update_jetpack_main_network_site_option() {
1170
		// do_action( 'add_option_$option', '$option', '$value-of-the-option' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1171
		/**
1172
		 * Fires when the site URL is updated.
1173
		 * Determines if the site is the main site of a Mulitiste network.
1174
		 *
1175
		 * @since 3.3.0
1176
		 *
1177
		 * @param string jetpack_main_network_site.
1178
		 * @param string network_site_url() Site URL for the "main" site of the current Multisite network.
1179
		 */
1180
		do_action( 'add_option_jetpack_main_network_site', 'jetpack_main_network_site', network_site_url() );
1181
		/**
1182
		 * Fires when the site URL is updated.
1183
		 * Determines if the is part of a multi network.
1184
		 *
1185
		 * @since 3.3.0
1186
		 *
1187
		 * @param string jetpack_is_main_network.
1188
		 * @param bool Jetpack::is_multi_network() Is the site part of a multi network.
1189
		 */
1190
		do_action( 'add_option_jetpack_is_main_network', 'jetpack_is_main_network', (string) (bool) Jetpack::is_multi_network() );
1191
		/**
1192
		 * Fires when the site URL is updated.
1193
		 * Determines if the site is part of a multisite network.
1194
		 *
1195
		 * @since 3.4.0
1196
		 *
1197
		 * @param string jetpack_is_multi_site.
1198
		 * @param bool is_multisite() Is the site part of a mutlisite network.
1199
		 */
1200
		do_action( 'add_option_jetpack_is_multi_site', 'jetpack_is_multi_site', (string) (bool) is_multisite() );
1201
	}
1202
	/**
1203
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1204
	 *
1205
	 */
1206
	function update_jetpack_network_settings() {
1207
		// Only sync this info for the main network site.
1208
		do_action( 'add_option_jetpack_network_name', 'jetpack_network_name', Jetpack::network_name() );
1209
		do_action( 'add_option_jetpack_network_allow_new_registrations', 'jetpack_network_allow_new_registrations', Jetpack::network_allow_new_registrations() );
1210
		do_action( 'add_option_jetpack_network_add_new_users', 'jetpack_network_add_new_users', Jetpack::network_add_new_users() );
1211
		do_action( 'add_option_jetpack_network_site_upload_space', 'jetpack_network_site_upload_space', Jetpack::network_site_upload_space() );
1212
		do_action( 'add_option_jetpack_network_upload_file_types', 'jetpack_network_upload_file_types', Jetpack::network_upload_file_types() );
1213
		do_action( 'add_option_jetpack_network_enable_administration_menus', 'jetpack_network_enable_administration_menus', Jetpack::network_enable_administration_menus() );
1214
1215
	}
1216
1217
	/**
1218
	 * Get back if the current site is single user site.
1219
	 *
1220
	 * @return bool
1221
	 */
1222
	public static function is_single_user_site() {
1223
1224
		$user_query = new WP_User_Query( array(
1225
			'blog_id' => get_current_blog_id(),
1226
			'fields'  => 'ID',
1227
			'number' => 2
1228
		) );
1229
		return 1 === (int) $user_query->get_total();
1230
	}
1231
1232
	/**
1233
	 * Returns true if the site has file write access false otherwise.
1234
	 * @return string ( '1' | '0' )
1235
	 **/
1236
	public static function file_system_write_access() {
1237
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1238
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
1239
		}
1240
1241
		require_once( ABSPATH . 'wp-admin/includes/template.php' );
1242
1243
		$filesystem_method = get_filesystem_method();
1244
		if ( $filesystem_method === 'direct' ) {
1245
			return 1;
1246
		}
1247
1248
		ob_start();
1249
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1250
		ob_end_clean();
1251
		if ( $filesystem_credentials_are_stored ) {
1252
			return 1;
1253
		}
1254
		return 0;
1255
	}
1256
1257
	/**
1258
	 * Finds out if a site is using a version control system.
1259
	 * @return string ( '1' | '0' )
1260
	 **/
1261
	public static function is_version_controlled() {
1262
1263
		if ( !class_exists( 'WP_Automatic_Updater' ) ) {
1264
			require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
1265
		}
1266
		$updater = new WP_Automatic_Updater();
1267
		$is_version_controlled = strval( $updater->is_vcs_checkout( $context = ABSPATH ) );
1268
		// transients should not be empty
1269
		if ( empty( $is_version_controlled ) ) {
1270
			$is_version_controlled = '0';
1271
		}
1272
		return $is_version_controlled;
1273
	}
1274
1275
	/**
1276
	 * Determines whether the current theme supports featured images or not.
1277
	 * @return string ( '1' | '0' )
1278
	 */
1279
	public static function featured_images_enabled() {
1280
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1281
	}
1282
1283
	/*
1284
	 * Sync back wp_version
1285
	 */
1286
	public static function get_wp_version() {
1287
		global $wp_version;
1288
		return $wp_version;
1289
	}
1290
1291
	/**
1292
	 * Keeps wp_version in sync with .com when WordPress core updates
1293
	 **/
1294
	public static function update_get_wp_version( $update, $meta_data ) {
1295
		if ( 'update' === $meta_data['action'] && 'core' === $meta_data['type'] ) {
1296
			/** This action is documented in wp-includes/option.php */
1297
			/**
1298
			 * This triggers the sync for the jetpack version
1299
			 * See Jetpack_Sync options method for more info.
1300
			 */
1301
			do_action( 'add_option_jetpack_wp_version', 'jetpack_wp_version', (string) Jetpack::get_wp_version() );
1302
		}
1303
	}
1304
1305
	/**
1306
	 * Triggers a sync of update counts and update details
1307
	 */
1308
	function sync_update_data() {
1309
		// Anytime WordPress saves update data, we'll want to sync update data
1310
		add_action( 'set_site_transient_update_plugins', array( 'Jetpack', 'refresh_update_data' ) );
1311
		add_action( 'set_site_transient_update_themes', array( 'Jetpack', 'refresh_update_data' ) );
1312
		add_action( 'set_site_transient_update_core', array( 'Jetpack', 'refresh_update_data' ) );
1313
		// Anytime a connection to jetpack is made, sync the update data
1314
		add_action( 'jetpack_site_registered', array( 'Jetpack', 'refresh_update_data' ) );
1315
		// Anytime the Jetpack Version changes, sync the the update data
1316
		add_action( 'updating_jetpack_version', array( 'Jetpack', 'refresh_update_data' ) );
1317
1318
		if ( current_user_can( 'update_core' ) && current_user_can( 'update_plugins' ) && current_user_can( 'update_themes' ) ) {
1319
			$this->sync->mock_option( 'updates', array( 'Jetpack', 'get_updates' ) );
1320
		}
1321
1322
		$this->sync->mock_option( 'update_details', array( 'Jetpack', 'get_update_details' ) );
1323
	}
1324
1325
	/**
1326
	 * Triggers a sync of information specific to the current theme.
1327
	 */
1328
	function sync_theme_data() {
1329
		add_action( 'switch_theme', array( 'Jetpack', 'refresh_theme_data' ) );
1330
		$this->sync->mock_option( 'featured_images_enabled', array( 'Jetpack', 'featured_images_enabled' ) );
1331
	}
1332
1333
	/**
1334
	 * jetpack_updates is saved in the following schema:
1335
	 *
1336
	 * array (
1337
	 *      'plugins'                       => (int) Number of plugin updates available.
1338
	 *      'themes'                        => (int) Number of theme updates available.
1339
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1340
	 *      'translations'                  => (int) Number of translation updates available.
1341
	 *      'total'                         => (int) Total of all available updates.
1342
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1343
	 * )
1344
	 * @return array
1345
	 */
1346
	public static function get_updates() {
1347
		$update_data = wp_get_update_data();
1348
1349
		// Stores the individual update counts as well as the total count.
1350
		if ( isset( $update_data['counts'] ) ) {
1351
			$updates = $update_data['counts'];
1352
		}
1353
1354
		// If we need to update WordPress core, let's find the latest version number.
1355 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1356
			$cur = get_preferred_from_update_core();
1357
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1358
				$updates['wp_update_version'] = $cur->current;
1359
			}
1360
		}
1361
		return isset( $updates ) ? $updates : array();
1362
	}
1363
1364
	public static function get_update_details() {
1365
		$update_details = array(
1366
			'update_core' => get_site_transient( 'update_core' ),
1367
			'update_plugins' => get_site_transient( 'update_plugins' ),
1368
			'update_themes' => get_site_transient( 'update_themes' ),
1369
		);
1370
		return $update_details;
1371
	}
1372
1373
	public static function refresh_update_data() {
1374
		if ( current_user_can( 'update_core' ) && current_user_can( 'update_plugins' ) && current_user_can( 'update_themes' ) ) {
1375
			/**
1376
			 * Fires whenever the amount of updates needed for a site changes.
1377
			 * Syncs an array that includes the number of theme, plugin, and core updates available, as well as the latest core version available.
1378
			 *
1379
			 * @since 3.7.0
1380
			 *
1381
			 * @param string jetpack_updates
1382
			 * @param array Update counts calculated by Jetpack::get_updates
1383
			 */
1384
			do_action( 'add_option_jetpack_updates', 'jetpack_updates', Jetpack::get_updates() );
1385
		}
1386
		/**
1387
		 * Fires whenever the amount of updates needed for a site changes.
1388
		 * Syncs an array of core, theme, and plugin data, and which of each is out of date
1389
		 *
1390
		 * @since 3.7.0
1391
		 *
1392
		 * @param string jetpack_update_details
1393
		 * @param array Update details calculated by Jetpack::get_update_details
1394
		 */
1395
		do_action( 'add_option_jetpack_update_details', 'jetpack_update_details', Jetpack::get_update_details() );
1396
	}
1397
1398
	public static function refresh_theme_data() {
1399
		/**
1400
		 * Fires whenever a theme change is made.
1401
		 *
1402
		 * @since 3.8.1
1403
		 *
1404
		 * @param string featured_images_enabled
1405
		 * @param boolean Whether featured images are enabled or not
1406
		 */
1407
		do_action( 'add_option_jetpack_featured_images_enabled', 'jetpack_featured_images_enabled', Jetpack::featured_images_enabled() );
1408
	}
1409
1410
	/**
1411
	 * Invalides the transient as well as triggers the update of the mock option.
1412
	 *
1413
	 * @return null
1414
	 */
1415
	function is_single_user_site_invalidate() {
1416
		/**
1417
		 * Fires when a user is added or removed from a site.
1418
		 * Determines if the site is a single user site.
1419
		 *
1420
		 * @since 3.4.0
1421
		 *
1422
		 * @param string jetpack_single_user_site.
1423
		 * @param bool Jetpack::is_single_user_site() Is the current site a single user site.
1424
		 */
1425
		do_action( 'update_option_jetpack_single_user_site', 'jetpack_single_user_site', (bool) Jetpack::is_single_user_site() );
1426
	}
1427
1428
	/**
1429
	 * Is Jetpack active?
1430
	 */
1431
	public static function is_active() {
1432
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1433
	}
1434
1435
	/**
1436
	 * Is Jetpack in development (offline) mode?
1437
	 */
1438
	public static function is_development_mode() {
1439
		$development_mode = false;
1440
1441
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1442
			$development_mode = JETPACK_DEV_DEBUG;
1443
		}
1444
1445
		elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1446
			$development_mode = true;
1447
		}
1448
		/**
1449
		 * Filters Jetpack's development mode.
1450
		 *
1451
		 * @see http://jetpack.me/support/development-mode/
1452
		 *
1453
		 * @since 2.2.1
1454
		 *
1455
		 * @param bool $development_mode Is Jetpack's development mode active.
1456
		 */
1457
		return apply_filters( 'jetpack_development_mode', $development_mode );
1458
	}
1459
1460
	/**
1461
	* Get Jetpack development mode notice text and notice class.
1462
	*
1463
	* Mirrors the checks made in Jetpack::is_development_mode
1464
	*
1465
	*/
1466
	public static function show_development_mode_notice() {
1467
		if ( Jetpack::is_development_mode() ) {
1468
			if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1469
				$notice = sprintf(
1470
					/* translators: %s is a URL */
1471
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the JETPACK_DEV_DEBUG constant being defined in wp-config.php or elsewhere.', 'jetpack' ),
1472
					'http://jetpack.me/support/development-mode/'
1473
				);
1474
			} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1475
				$notice = sprintf(
1476
					/* translators: %s is a URL */
1477
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via site URL lacking a dot (e.g. http://localhost).', 'jetpack' ),
1478
					'http://jetpack.me/support/development-mode/'
1479
				);
1480
			} else {
1481
				$notice = sprintf(
1482
					/* translators: %s is a URL */
1483
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the jetpack_development_mode filter.', 'jetpack' ),
1484
					'http://jetpack.me/support/development-mode/'
1485
				);
1486
			}
1487
1488
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1489
		}
1490
1491
		// Throw up a notice if using a development version and as for feedback.
1492
		if ( Jetpack::is_development_version() ) {
1493
			/* translators: %s is a URL */
1494
			$notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), 'https://jetpack.me/contact-support/beta-group/' );
1495
1496
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1497
		}
1498
	}
1499
1500
	/**
1501
	 * Whether Jetpack's version maps to a public release, or a development version.
1502
	 */
1503
	public static function is_development_version() {
1504
		return ! preg_match( '/^\d+(\.\d+)+$/', JETPACK__VERSION );
1505
	}
1506
1507
	/**
1508
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1509
	 */
1510
	public static function is_user_connected( $user_id = false ) {
1511
		$user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
1512
		if ( ! $user_id ) {
1513
			return false;
1514
		}
1515
		return (bool) Jetpack_Data::get_access_token( $user_id );
1516
	}
1517
1518
	/**
1519
	 * Get the wpcom user data of the current|specified connected user.
1520
	 */
1521 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1522
		if ( ! $user_id ) {
1523
			$user_id = get_current_user_id();
1524
		}
1525
		Jetpack::load_xml_rpc_client();
1526
		$xml = new Jetpack_IXR_Client( array(
1527
			'user_id' => $user_id,
1528
		) );
1529
		$xml->query( 'wpcom.getUser' );
1530
		if ( ! $xml->isError() ) {
1531
			return $xml->getResponse();
1532
		}
1533
		return false;
1534
	}
1535
1536
	/**
1537
	 * Get the wpcom email of the current|specified connected user.
1538
	 */
1539 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1540
		if ( ! $user_id ) {
1541
			$user_id = get_current_user_id();
1542
		}
1543
		Jetpack::load_xml_rpc_client();
1544
		$xml = new Jetpack_IXR_Client( array(
1545
			'user_id' => $user_id,
1546
		) );
1547
		$xml->query( 'wpcom.getUserEmail' );
1548
		if ( ! $xml->isError() ) {
1549
			return $xml->getResponse();
1550
		}
1551
		return false;
1552
	}
1553
1554
	/**
1555
	 * Get the wpcom email of the master user.
1556
	 */
1557
	public static function get_master_user_email() {
1558
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1559
		if ( $master_user_id ) {
1560
			return self::get_connected_user_email( $master_user_id );
1561
		}
1562
		return '';
1563
	}
1564
1565
	function current_user_is_connection_owner() {
1566
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1567
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1568
	}
1569
1570
	/**
1571
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1572
	 */
1573
	function extra_oembed_providers() {
1574
		// Cloudup: https://dev.cloudup.com/#oembed
1575
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1576
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1577
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1578
	}
1579
1580
	/**
1581
	 * Synchronize connected user role changes
1582
	 */
1583
	function user_role_change( $user_id ) {
1584
		if ( Jetpack::is_active() && Jetpack::is_user_connected( $user_id ) ) {
1585
			$current_user_id = get_current_user_id();
1586
			wp_set_current_user( $user_id );
1587
			$role = $this->translate_current_user_to_role();
1588
			$signed_role = $this->sign_role( $role );
1589
			wp_set_current_user( $current_user_id );
1590
1591
			$master_token   = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1592
			$master_user_id = absint( $master_token->external_user_id );
1593
1594
			if ( ! $master_user_id )
1595
				return; // this shouldn't happen
1596
1597
			Jetpack::xmlrpc_async_call( 'jetpack.updateRole', $user_id, $signed_role );
1598
			//@todo retry on failure
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1599
1600
			//try to choose a new master if we're demoting the current one
1601
			if ( $user_id == $master_user_id && 'administrator' != $role ) {
1602
				$query = new WP_User_Query(
1603
					array(
1604
						'fields'  => array( 'id' ),
1605
						'role'    => 'administrator',
1606
						'orderby' => 'id',
1607
						'exclude' => array( $master_user_id ),
1608
					)
1609
				);
1610
				$new_master = false;
1611
				foreach ( $query->results as $result ) {
1612
					$uid = absint( $result->id );
1613
					if ( $uid && Jetpack::is_user_connected( $uid ) ) {
1614
						$new_master = $uid;
1615
						break;
1616
					}
1617
				}
1618
1619
				if ( $new_master ) {
1620
					Jetpack_Options::update_option( 'master_user', $new_master );
1621
				}
1622
				// else disconnect..?
1623
			}
1624
		}
1625
	}
1626
1627
	/**
1628
	 * Loads the currently active modules.
1629
	 */
1630
	public static function load_modules() {
1631
		if ( ! self::is_active() && !self::is_development_mode() ) {
1632
			if ( ! is_multisite() || ! get_site_option( 'jetpack_protect_active' ) ) {
1633
				return;
1634
			}
1635
		}
1636
1637
		$version = Jetpack_Options::get_option( 'version' );
1638 View Code Duplication
		if ( ! $version ) {
1639
			$version = $old_version = JETPACK__VERSION . ':' . time();
1640
			/** This action is documented in class.jetpack.php */
1641
			do_action( 'updating_jetpack_version', $version, false );
1642
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1643
		}
1644
		list( $version ) = explode( ':', $version );
1645
1646
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1647
1648
		$modules_data = array();
1649
1650
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1651
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1652
			$updated_modules = array();
1653
			foreach ( $modules as $module ) {
1654
				$modules_data[ $module ] = Jetpack::get_module( $module );
1655
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1656
					continue;
1657
				}
1658
1659
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1660
					continue;
1661
				}
1662
1663
				$updated_modules[] = $module;
1664
			}
1665
1666
			$modules = array_diff( $modules, $updated_modules );
1667
		}
1668
1669
		$is_development_mode = Jetpack::is_development_mode();
1670
1671
		foreach ( $modules as $module ) {
1672
			// If we're in dev mode, disable modules requiring a connection
1673
			if ( $is_development_mode ) {
1674
				// Prime the pump if we need to
1675
				if ( empty( $modules_data[ $module ] ) ) {
1676
					$modules_data[ $module ] = Jetpack::get_module( $module );
1677
				}
1678
				// If the module requires a connection, but we're in local mode, don't include it.
1679
				if ( $modules_data[ $module ]['requires_connection'] ) {
1680
					continue;
1681
				}
1682
			}
1683
1684
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1685
				continue;
1686
			}
1687
1688
			require Jetpack::get_module_path( $module );
1689
			/**
1690
			 * Fires when a specific module is loaded.
1691
			 * The dynamic part of the hook, $module, is the module slug.
1692
			 *
1693
			 * @since 1.1.0
1694
			 */
1695
			do_action( 'jetpack_module_loaded_' . $module );
1696
		}
1697
1698
		/**
1699
		 * Fires when all the modules are loaded.
1700
		 *
1701
		 * @since 1.1.0
1702
		 */
1703
		do_action( 'jetpack_modules_loaded' );
1704
1705
		// 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.
1706
		if ( Jetpack::is_active() || Jetpack::is_development_mode() )
1707
			require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
1708
	}
1709
1710
	/**
1711
	 * Check if Jetpack's REST API compat file should be included
1712
	 * @action plugins_loaded
1713
	 * @return null
1714
	 */
1715
	public function check_rest_api_compat() {
1716
		/**
1717
		 * Filters the list of REST API compat files to be included.
1718
		 *
1719
		 * @since 2.2.5
1720
		 *
1721
		 * @param array $args Array of REST API compat files to include.
1722
		 */
1723
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1724
1725
		if ( function_exists( 'bbpress' ) )
1726
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
1727
1728
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
1729
			require_once $_jetpack_rest_api_compat_include;
1730
	}
1731
1732
	/**
1733
	 * Gets all plugins currently active in values, regardless of whether they're
1734
	 * traditionally activated or network activated.
1735
	 *
1736
	 * @todo Store the result in core's object cache maybe?
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
1737
	 */
1738
	public static function get_active_plugins() {
1739
		$active_plugins = (array) get_option( 'active_plugins', array() );
1740
1741
		if ( is_multisite() ) {
1742
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1743
			// whereas active_plugins stores them in the values.
1744
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1745
			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...
1746
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1747
			}
1748
		}
1749
1750
		sort( $active_plugins );
1751
1752
		return array_unique( $active_plugins );
1753
	}
1754
1755
	/**
1756
	 * Gets and parses additional plugin data to send with the heartbeat data
1757
	 *
1758
	 * @since 3.8.1
1759
	 *
1760
	 * @return array Array of plugin data
1761
	 */
1762
	public static function get_parsed_plugin_data() {
1763
		if ( ! function_exists( 'get_plugins' ) ) {
1764
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1765
		}
1766
		$all_plugins    = get_plugins();
1767
		$active_plugins = Jetpack::get_active_plugins();
1768
1769
		$plugins = array();
1770
		foreach ( $all_plugins as $path => $plugin_data ) {
1771
			$plugins[ $path ] = array(
1772
					'is_active' => in_array( $path, $active_plugins ),
1773
					'file'      => $path,
1774
					'name'      => $plugin_data['Name'],
1775
					'version'   => $plugin_data['Version'],
1776
					'author'    => $plugin_data['Author'],
1777
			);
1778
		}
1779
1780
		return $plugins;
1781
	}
1782
1783
	/**
1784
	 * Gets and parses theme data to send with the heartbeat data
1785
	 *
1786
	 * @since 3.8.1
1787
	 *
1788
	 * @return array Array of theme data
1789
	 */
1790
	public static function get_parsed_theme_data() {
1791
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
1792
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
1793
1794
		$themes = array();
1795
		foreach ( $all_themes as $slug => $theme_data ) {
1796
			$theme_headers = array();
1797
			foreach ( $header_keys as $header_key ) {
1798
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
1799
			}
1800
1801
			$themes[ $slug ] = array(
1802
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
1803
					'slug' => $slug,
1804
					'theme_root' => $theme_data->get_theme_root_uri(),
1805
					'parent' => $theme_data->parent(),
1806
					'headers' => $theme_headers
1807
			);
1808
		}
1809
1810
		return $themes;
1811
	}
1812
1813
	/**
1814
	 * Checks whether a specific plugin is active.
1815
	 *
1816
	 * We don't want to store these in a static variable, in case
1817
	 * there are switch_to_blog() calls involved.
1818
	 */
1819
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
1820
		return in_array( $plugin, self::get_active_plugins() );
1821
	}
1822
1823
	/**
1824
	 * Check if Jetpack's Open Graph tags should be used.
1825
	 * If certain plugins are active, Jetpack's og tags are suppressed.
1826
	 *
1827
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1828
	 * @action plugins_loaded
1829
	 * @return null
1830
	 */
1831
	public function check_open_graph() {
1832
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
1833
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
1834
		}
1835
1836
		$active_plugins = self::get_active_plugins();
1837
1838
		if ( ! empty( $active_plugins ) ) {
1839
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
1840
				if ( in_array( $plugin, $active_plugins ) ) {
1841
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
1842
					break;
1843
				}
1844
			}
1845
		}
1846
1847
		/**
1848
		 * Allow the addition of Open Graph Meta Tags to all pages.
1849
		 *
1850
		 * @since 2.0.3
1851
		 *
1852
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
1853
		 */
1854
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
1855
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
1856
		}
1857
	}
1858
1859
	/**
1860
	 * Check if Jetpack's Twitter tags should be used.
1861
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
1862
	 *
1863
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1864
	 * @action plugins_loaded
1865
	 * @return null
1866
	 */
1867
	public function check_twitter_tags() {
1868
1869
		$active_plugins = self::get_active_plugins();
1870
1871
		if ( ! empty( $active_plugins ) ) {
1872
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
1873
				if ( in_array( $plugin, $active_plugins ) ) {
1874
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
1875
					break;
1876
				}
1877
			}
1878
		}
1879
1880
		/**
1881
		 * Allow Twitter Card Meta tags to be disabled.
1882
		 *
1883
		 * @since 2.6.0
1884
		 *
1885
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
1886
		 */
1887
		if ( apply_filters( 'jetpack_disable_twitter_cards', true ) ) {
1888
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
1889
		}
1890
	}
1891
1892
1893
1894
1895
	/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1896
	 *
1897
	 * Jetpack Security Reports
1898
	 *
1899
	 * Allowed types: login_form, backup, file_scanning, spam
1900
	 *
1901
	 * Args for login_form and spam: 'blocked'=>(int)(optional), 'status'=>(string)(ok, warning, error), 'message'=>(optional, disregarded if status is ok, allowed tags: a, em, strong)
1902
	 *
1903
	 * Args for backup and file_scanning: 'last'=>(timestamp)(optional), 'next'=>(timestamp)(optional), 'status'=>(string)(ok, warning, error), 'message'=>(optional, disregarded if status is ok, allowed tags: a, em, strong)
1904
	 *
1905
	 *
1906
	 * Example code to submit a security report:
1907
	 *
1908
	 *  function akismet_submit_jetpack_security_report() {
1909
	 *  	Jetpack::submit_security_report( 'spam', __FILE__, $args = array( 'blocked' => 138284, status => 'ok' ) );
1910
	 *  }
1911
	 *  add_action( 'jetpack_security_report', 'akismet_submit_jetpack_security_report' );
1912
	 *
1913
	 */
1914
1915
1916
	/**
1917
	 * Calls for security report submissions.
1918
	 *
1919
	 * @return null
1920
	 */
1921
	public static function perform_security_reporting() {
1922
		$no_check_needed = get_site_transient( 'security_report_performed_recently' );
1923
1924
		if ( $no_check_needed ) {
1925
			return;
1926
		}
1927
1928
		/**
1929
		 * Fires before a security report is created.
1930
		 *
1931
		 * @since 3.4.0
1932
		 */
1933
		do_action( 'jetpack_security_report' );
1934
1935
		Jetpack_Options::update_option( 'security_report', self::$security_report );
1936
		set_site_transient( 'security_report_performed_recently', 1, 15 * MINUTE_IN_SECONDS );
1937
	}
1938
1939
	/**
1940
	 * Allows plugins to submit security reports.
1941
 	 *
1942
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
1943
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
1944
	 * @param array   $args         See definitions above
1945
	 */
1946
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
1947
1948
		if( !doing_action( 'jetpack_security_report' ) ) {
1949
			return new WP_Error( 'not_collecting_report', 'Not currently collecting security reports.  Please use the jetpack_security_report hook.' );
1950
		}
1951
1952
		if( !is_string( $type ) || !is_string( $plugin_file ) ) {
1953
			return new WP_Error( 'invalid_security_report', 'Invalid Security Report' );
1954
		}
1955
1956
		if( !function_exists( 'get_plugin_data' ) ) {
1957
			include( ABSPATH . 'wp-admin/includes/plugin.php' );
1958
		}
1959
1960
		//Get rid of any non-allowed args
1961
		$args = array_intersect_key( $args, array_flip( array( 'blocked', 'last', 'next', 'status', 'message' ) ) );
1962
1963
		$plugin = get_plugin_data( $plugin_file );
1964
1965
		if ( !$plugin['Name'] ) {
1966
			return new WP_Error( 'security_report_missing_plugin_name', 'Invalid Plugin File Provided' );
1967
		}
1968
1969
		// Sanitize everything to make sure we're not syncing something wonky
1970
		$type = sanitize_key( $type );
1971
1972
		$args['plugin'] = $plugin;
1973
1974
		// Cast blocked, last and next as integers.
1975
		// Last and next should be in unix timestamp format
1976
		if ( isset( $args['blocked'] ) ) {
1977
			$args['blocked'] = (int) $args['blocked'];
1978
		}
1979
		if ( isset( $args['last'] ) ) {
1980
			$args['last'] = (int) $args['last'];
1981
		}
1982
		if ( isset( $args['next'] ) ) {
1983
			$args['next'] = (int) $args['next'];
1984
		}
1985
		if ( !in_array( $args['status'], array( 'ok', 'warning', 'error' ) ) ) {
1986
			$args['status'] = 'ok';
1987
		}
1988
		if ( isset( $args['message'] ) ) {
1989
1990
			if( $args['status'] == 'ok' ) {
1991
				unset( $args['message'] );
1992
			}
1993
1994
			$allowed_html = array(
1995
			    'a' => array(
1996
			        'href' => array(),
1997
			        'title' => array()
1998
			    ),
1999
			    'em' => array(),
2000
			    'strong' => array(),
2001
			);
2002
2003
			$args['message'] = wp_kses( $args['message'], $allowed_html );
2004
		}
2005
2006
		$plugin_name = $plugin[ 'Name' ];
2007
2008
		self::$security_report[ $type ][ $plugin_name ] = $args;
2009
	}
2010
2011
	/**
2012
	 * Collects a new report if needed, then returns it.
2013
	 */
2014
	public function get_security_report() {
2015
		self::perform_security_reporting();
2016
		return Jetpack_Options::get_option( 'security_report' );
2017
	}
2018
2019
2020
/* Jetpack Options API */
2021
2022
	public static function get_option_names( $type = 'compact' ) {
2023
		return Jetpack_Options::get_option_names( $type );
2024
	}
2025
2026
	/**
2027
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2028
 	 *
2029
	 * @param string $name    Option name
2030
	 * @param mixed  $default (optional)
2031
	 */
2032
	public static function get_option( $name, $default = false ) {
2033
		return Jetpack_Options::get_option( $name, $default );
2034
	}
2035
2036
	/**
2037
	* Stores two secrets and a timestamp so WordPress.com can make a request back and verify an action
2038
	* Does some extra verification so urls (such as those to public-api, register, etc) can't just be crafted
2039
	* $name must be a registered option name.
2040
	*/
2041
	public static function create_nonce( $name ) {
2042
		$secret = wp_generate_password( 32, false ) . ':' . wp_generate_password( 32, false ) . ':' . ( time() + 600 );
2043
2044
		Jetpack_Options::update_option( $name, $secret );
2045
		@list( $secret_1, $secret_2, $eol ) = explode( ':', Jetpack_Options::get_option( $name ) );
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...
2046
		if ( empty( $secret_1 ) || empty( $secret_2 ) || $eol < time() )
2047
			return new Jetpack_Error( 'missing_secrets' );
2048
2049
		return array(
2050
			'secret_1' => $secret_1,
2051
			'secret_2' => $secret_2,
2052
			'eol'      => $eol,
2053
		);
2054
	}
2055
2056
	/**
2057
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2058
 	 *
2059
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2060
	 * @param string $name  Option name
2061
	 * @param mixed  $value Option value
2062
	 */
2063
	public static function update_option( $name, $value ) {
2064
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2065
		return Jetpack_Options::update_option( $name, $value );
2066
	}
2067
2068
	/**
2069
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2070
 	 *
2071
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2072
	 * @param array $array array( option name => option value, ... )
2073
	 */
2074
	public static function update_options( $array ) {
2075
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2076
		return Jetpack_Options::update_options( $array );
2077
	}
2078
2079
	/**
2080
	 * Deletes the given option.  May be passed multiple option names as an array.
2081
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2082
	 *
2083
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2084
	 * @param string|array $names
2085
	 */
2086
	public static function delete_option( $names ) {
2087
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2088
		return Jetpack_Options::delete_option( $names );
2089
	}
2090
2091
	/**
2092
	 * Enters a user token into the user_tokens option
2093
	 *
2094
	 * @param int $user_id
2095
	 * @param string $token
2096
	 * return bool
2097
	 */
2098
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2099
		// not designed for concurrent updates
2100
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
2101
		if ( ! is_array( $user_tokens ) )
2102
			$user_tokens = array();
2103
		$user_tokens[$user_id] = $token;
2104
		if ( $is_master_user ) {
2105
			$master_user = $user_id;
2106
			$options     = compact( 'user_tokens', 'master_user' );
2107
		} else {
2108
			$options = compact( 'user_tokens' );
2109
		}
2110
		return Jetpack_Options::update_options( $options );
2111
	}
2112
2113
	/**
2114
	 * Returns an array of all PHP files in the specified absolute path.
2115
	 * Equivalent to glob( "$absolute_path/*.php" ).
2116
	 *
2117
	 * @param string $absolute_path The absolute path of the directory to search.
2118
	 * @return array Array of absolute paths to the PHP files.
2119
	 */
2120
	public static function glob_php( $absolute_path ) {
2121
		if ( function_exists( 'glob' ) ) {
2122
			return glob( "$absolute_path/*.php" );
2123
		}
2124
2125
		$absolute_path = untrailingslashit( $absolute_path );
2126
		$files = array();
2127
		if ( ! $dir = @opendir( $absolute_path ) ) {
2128
			return $files;
2129
		}
2130
2131
		while ( false !== $file = readdir( $dir ) ) {
2132
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2133
				continue;
2134
			}
2135
2136
			$file = "$absolute_path/$file";
2137
2138
			if ( ! is_file( $file ) ) {
2139
				continue;
2140
			}
2141
2142
			$files[] = $file;
2143
		}
2144
2145
		closedir( $dir );
2146
2147
		return $files;
2148
	}
2149
2150
	public static function activate_new_modules( $redirect = false ) {
2151
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
2152
			return;
2153
		}
2154
2155
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2156 View Code Duplication
		if ( ! $jetpack_old_version ) {
2157
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2158
			/** This action is documented in class.jetpack.php */
2159
			do_action( 'updating_jetpack_version', $version, false );
2160
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2161
		}
2162
2163
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2164
2165
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2166
			return;
2167
		}
2168
2169
		$active_modules     = Jetpack::get_active_modules();
2170
		$reactivate_modules = array();
2171
		foreach ( $active_modules as $active_module ) {
2172
			$module = Jetpack::get_module( $active_module );
2173
			if ( ! isset( $module['changed'] ) ) {
2174
				continue;
2175
			}
2176
2177
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2178
				continue;
2179
			}
2180
2181
			$reactivate_modules[] = $active_module;
2182
			Jetpack::deactivate_module( $active_module );
2183
		}
2184
2185
		$new_version = JETPACK__VERSION . ':' . time();
2186
		/** This action is documented in class.jetpack.php */
2187
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2188
		Jetpack_Options::update_options(
2189
			array(
2190
				'version'     => $new_version,
2191
				'old_version' => $jetpack_old_version,
2192
			)
2193
		);
2194
2195
		Jetpack::state( 'message', 'modules_activated' );
2196
		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...
2197
2198
		if ( $redirect ) {
2199
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2200
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2201
				$page = $_GET['page'];
2202
			}
2203
2204
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
2205
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method activate_new_modules() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2206
		}
2207
	}
2208
2209
	/**
2210
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2211
	 * Make sure to tuck away module "library" files in a sub-directory.
2212
	 */
2213
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2214
		static $modules = null;
2215
2216
		if ( ! isset( $modules ) ) {
2217
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2218
			// Use the cache if we're on the front-end and it's available...
2219
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2220
				$modules = $available_modules_option[ JETPACK__VERSION ];
2221
			} else {
2222
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2223
2224
				$modules = array();
2225
2226
				foreach ( $files as $file ) {
2227
					if ( ! $headers = Jetpack::get_module( $file ) ) {
2228
						continue;
2229
					}
2230
2231
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
2232
				}
2233
2234
				Jetpack_Options::update_option( 'available_modules', array(
2235
					JETPACK__VERSION => $modules,
2236
				) );
2237
			}
2238
		}
2239
2240
		/**
2241
		 * Filters the array of modules available to be activated.
2242
		 *
2243
		 * @since 2.4.0
2244
		 *
2245
		 * @param array $modules Array of available modules.
2246
		 * @param string $min_version Minimum version number required to use modules.
2247
		 * @param string $max_version Maximum version number required to use modules.
2248
		 */
2249
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
2250
2251
		if ( ! $min_version && ! $max_version ) {
2252
			return array_keys( $mods );
2253
		}
2254
2255
		$r = array();
2256
		foreach ( $mods as $slug => $introduced ) {
2257
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2258
				continue;
2259
			}
2260
2261
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2262
				continue;
2263
			}
2264
2265
			$r[] = $slug;
2266
		}
2267
2268
		return $r;
2269
	}
2270
2271
	/**
2272
	 * Default modules loaded on activation.
2273
	 */
2274
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2275
		$return = array();
2276
2277
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
2278
			$module_data = Jetpack::get_module( $module );
2279
2280
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2281
				case 'yes' :
2282
					$return[] = $module;
2283
					break;
2284
				case 'public' :
2285
					if ( Jetpack_Options::get_option( 'public' ) ) {
2286
						$return[] = $module;
2287
					}
2288
					break;
2289
				case 'no' :
2290
				default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2291
					break;
2292
			}
2293
		}
2294
		/**
2295
		 * Filters the array of default modules.
2296
		 *
2297
		 * @since 2.5.0
2298
		 *
2299
		 * @param array $return Array of default modules.
2300
		 * @param string $min_version Minimum version number required to use modules.
2301
		 * @param string $max_version Maximum version number required to use modules.
2302
		 */
2303
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
2304
	}
2305
2306
	/**
2307
	 * Checks activated modules during auto-activation to determine
2308
	 * if any of those modules are being deprecated.  If so, close
2309
	 * them out, and add any replacement modules.
2310
	 *
2311
	 * Runs at priority 99 by default.
2312
	 *
2313
	 * This is run late, so that it can still activate a module if
2314
	 * the new module is a replacement for another that the user
2315
	 * currently has active, even if something at the normal priority
2316
	 * would kibosh everything.
2317
	 *
2318
	 * @since 2.6
2319
	 * @uses jetpack_get_default_modules filter
2320
	 * @param array $modules
2321
	 * @return array
2322
	 */
2323
	function handle_deprecated_modules( $modules ) {
2324
		$deprecated_modules = array(
2325
			'debug'            => null,  // Closed out and moved to ./class.jetpack-debugger.php
2326
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2327
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2328
		);
2329
2330
		// Don't activate SSO if they never completed activating WPCC.
2331
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
2332
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2333
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2334
				$deprecated_modules['wpcc'] = null;
2335
			}
2336
		}
2337
2338
		foreach ( $deprecated_modules as $module => $replacement ) {
2339
			if ( Jetpack::is_module_active( $module ) ) {
2340
				self::deactivate_module( $module );
2341
				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...
2342
					$modules[] = $replacement;
2343
				}
2344
			}
2345
		}
2346
2347
		return array_unique( $modules );
2348
	}
2349
2350
	/**
2351
	 * Checks activated plugins during auto-activation to determine
2352
	 * if any of those plugins are in the list with a corresponding module
2353
	 * that is not compatible with the plugin. The module will not be allowed
2354
	 * to auto-activate.
2355
	 *
2356
	 * @since 2.6
2357
	 * @uses jetpack_get_default_modules filter
2358
	 * @param array $modules
2359
	 * @return array
2360
	 */
2361
	function filter_default_modules( $modules ) {
2362
2363
		$active_plugins = self::get_active_plugins();
2364
2365
		if ( ! empty( $active_plugins ) ) {
2366
2367
			// For each module we'd like to auto-activate...
2368
			foreach ( $modules as $key => $module ) {
2369
				// If there are potential conflicts for it...
2370
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2371
					// For each potential conflict...
2372
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2373
						// If that conflicting plugin is active...
2374
						if ( in_array( $plugin, $active_plugins ) ) {
2375
							// Remove that item from being auto-activated.
2376
							unset( $modules[ $key ] );
2377
						}
2378
					}
2379
				}
2380
			}
2381
		}
2382
2383
		return $modules;
2384
	}
2385
2386
	/**
2387
	 * Extract a module's slug from its full path.
2388
	 */
2389
	public static function get_module_slug( $file ) {
2390
		return str_replace( '.php', '', basename( $file ) );
2391
	}
2392
2393
	/**
2394
	 * Generate a module's path from its slug.
2395
	 */
2396
	public static function get_module_path( $slug ) {
2397
		return JETPACK__PLUGIN_DIR . "modules/$slug.php";
2398
	}
2399
2400
	/**
2401
	 * Load module data from module file. Headers differ from WordPress
2402
	 * plugin headers to avoid them being identified as standalone
2403
	 * plugins on the WordPress plugins page.
2404
	 */
2405
	public static function get_module( $module ) {
2406
		$headers = array(
2407
			'name'                      => 'Module Name',
2408
			'description'               => 'Module Description',
2409
			'jumpstart_desc'            => 'Jumpstart Description',
2410
			'sort'                      => 'Sort Order',
2411
			'recommendation_order'      => 'Recommendation Order',
2412
			'introduced'                => 'First Introduced',
2413
			'changed'                   => 'Major Changes In',
2414
			'deactivate'                => 'Deactivate',
2415
			'free'                      => 'Free',
2416
			'requires_connection'       => 'Requires Connection',
2417
			'auto_activate'             => 'Auto Activate',
2418
			'module_tags'               => 'Module Tags',
2419
			'feature'                   => 'Feature',
2420
			'additional_search_queries' => 'Additional Search Queries',
2421
		);
2422
2423
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2424
2425
		$mod = Jetpack::get_file_data( $file, $headers );
2426
		if ( empty( $mod['name'] ) ) {
2427
			return false;
2428
		}
2429
2430
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2431
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2432
		$mod['deactivate']              = empty( $mod['deactivate'] );
2433
		$mod['free']                    = empty( $mod['free'] );
2434
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2435
2436
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2437
			$mod['auto_activate'] = 'No';
2438
		} else {
2439
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2440
		}
2441
2442
		if ( $mod['module_tags'] ) {
2443
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2444
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2445
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2446
		} else {
2447
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2448
		}
2449
2450
		if ( $mod['feature'] ) {
2451
			$mod['feature'] = explode( ',', $mod['feature'] );
2452
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2453
		} else {
2454
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2455
		}
2456
2457
		/**
2458
		 * Filters the feature array on a module.
2459
		 *
2460
		 * This filter allows you to control where each module is filtered: Recommended,
2461
		 * Jumpstart, and the default "Other" listing.
2462
		 *
2463
		 * @since 3.5.0
2464
		 *
2465
		 * @param array   $mod['feature'] The areas to feature this module:
2466
		 *     'Jumpstart' adds to the "Jumpstart" option to activate many modules at once.
2467
		 *     'Recommended' shows on the main Jetpack admin screen.
2468
		 *     'Other' should be the default if no other value is in the array.
2469
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2470
		 * @param array   $mod All the currently assembled module data.
2471
		 */
2472
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2473
2474
		/**
2475
		 * Filter the returned data about a module.
2476
		 *
2477
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2478
		 * so please be careful.
2479
		 *
2480
		 * @since 3.6.0
2481
		 *
2482
		 * @param array   $mod    The details of the requested module.
2483
		 * @param string  $module The slug of the module, e.g. sharedaddy
2484
		 * @param string  $file   The path to the module source file.
2485
		 */
2486
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2487
	}
2488
2489
	/**
2490
	 * Like core's get_file_data implementation, but caches the result.
2491
	 */
2492
	public static function get_file_data( $file, $headers ) {
2493
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2494
		$file_name = basename( $file );
2495
		$file_data_option = Jetpack_Options::get_option( 'file_data', array() );
2496
		$key              = md5( $file_name . serialize( $headers ) );
2497
		$refresh_cache    = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2498
2499
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2500
		if ( ! $refresh_cache && isset( $file_data_option[ JETPACK__VERSION ][ $key ] ) ) {
2501
			return $file_data_option[ JETPACK__VERSION ][ $key ];
2502
		}
2503
2504
		$data = get_file_data( $file, $headers );
2505
2506
		// Strip out any old Jetpack versions that are cluttering the option.
2507
		$file_data_option = array_intersect_key( (array) $file_data_option, array( JETPACK__VERSION => null ) );
2508
		$file_data_option[ JETPACK__VERSION ][ $key ] = $data;
2509
		Jetpack_Options::update_option( 'file_data', $file_data_option );
2510
2511
		return $data;
2512
	}
2513
2514
	public static function translate_module_tag( $untranslated_tag ) {
2515
		// Tags are aggregated by tools/build-module-headings-translations.php
2516
		// and output in modules/module-headings.php
2517
		return _x( $untranslated_tag, 'Module Tag', 'jetpack' );
2518
	}
2519
2520
	/**
2521
	 * Get a list of activated modules as an array of module slugs.
2522
	 */
2523
	public static function get_active_modules() {
2524
		$active = Jetpack_Options::get_option( 'active_modules' );
2525
		if ( ! is_array( $active ) )
2526
			$active = array();
2527
		if ( is_admin() && ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) ) {
2528
			$active[] = 'vaultpress';
2529
		} else {
2530
			$active = array_diff( $active, array( 'vaultpress' ) );
2531
		}
2532
2533
		//If protect is active on the main site of a multisite, it should be active on all sites.
2534
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2535
			$active[] = 'protect';
2536
		}
2537
2538
		return array_unique( $active );
2539
	}
2540
2541
	/**
2542
	 * Check whether or not a Jetpack module is active.
2543
	 *
2544
	 * @param string $module The slug of a Jetpack module.
2545
	 * @return bool
2546
	 *
2547
	 * @static
2548
	 */
2549
	public static function is_module_active( $module ) {
2550
		return in_array( $module, self::get_active_modules() );
2551
	}
2552
2553
	public static function is_module( $module ) {
2554
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2555
	}
2556
2557
	/**
2558
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2559
	 *
2560
	 * @param bool $catch True to start catching, False to stop.
2561
	 *
2562
	 * @static
2563
	 */
2564
	public static function catch_errors( $catch ) {
2565
		static $display_errors, $error_reporting;
2566
2567
		if ( $catch ) {
2568
			$display_errors  = @ini_set( 'display_errors', 1 );
2569
			$error_reporting = @error_reporting( E_ALL );
2570
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2571
		} else {
2572
			@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...
2573
			@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...
2574
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2575
		}
2576
	}
2577
2578
	/**
2579
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2580
	 */
2581
	public static function catch_errors_on_shutdown() {
2582
		Jetpack::state( 'php_errors', ob_get_clean() );
2583
	}
2584
2585
	public static function activate_default_modules( $min_version = false, $max_version = false, $other_modules = array() ) {
2586
		$jetpack = Jetpack::init();
2587
2588
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2589
		$modules = array_merge( $other_modules, $modules );
2590
2591
		// Look for standalone plugins and disable if active.
2592
2593
		$to_deactivate = array();
2594
		foreach ( $modules as $module ) {
2595
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2596
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2597
			}
2598
		}
2599
2600
		$deactivated = array();
2601
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2602
			list( $probable_file, $probable_title ) = $deactivate_me;
2603
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2604
				$deactivated[] = $module;
2605
			}
2606
		}
2607
2608
		if ( $deactivated ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2609
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2610
2611
			$url = add_query_arg(
2612
				array(
2613
					'action'   => 'activate_default_modules',
2614
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2615
				),
2616
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2617
			);
2618
			wp_safe_redirect( $url );
2619
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method activate_default_modules() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2620
		}
2621
2622
		/**
2623
		 * Fires before default modules are activated.
2624
		 *
2625
		 * @since 1.9.0
2626
		 *
2627
		 * @param string $min_version Minimum version number required to use modules.
2628
		 * @param string $max_version Maximum version number required to use modules.
2629
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2630
		 */
2631
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2632
2633
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2634
		Jetpack::restate();
2635
		Jetpack::catch_errors( true );
2636
2637
		$active = Jetpack::get_active_modules();
2638
2639
		foreach ( $modules as $module ) {
2640
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2641
				$active[] = $module;
2642
				Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2643
				continue;
2644
			}
2645
2646
			if ( in_array( $module, $active ) ) {
2647
				$module_info = Jetpack::get_module( $module );
2648
				if ( ! $module_info['deactivate'] ) {
2649
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2650 View Code Duplication
					if ( $active_state = Jetpack::state( $state ) ) {
2651
						$active_state = explode( ',', $active_state );
2652
					} else {
2653
						$active_state = array();
2654
					}
2655
					$active_state[] = $module;
2656
					Jetpack::state( $state, implode( ',', $active_state ) );
2657
				}
2658
				continue;
2659
			}
2660
2661
			$file = Jetpack::get_module_path( $module );
2662
			if ( ! file_exists( $file ) ) {
2663
				continue;
2664
			}
2665
2666
			// we'll override this later if the plugin can be included without fatal error
2667
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2668
			Jetpack::state( 'error', 'module_activation_failed' );
2669
			Jetpack::state( 'module', $module );
2670
			ob_start();
2671
			require $file;
2672
			/**
2673
			 * Fires when a specific module is activated.
2674
			 *
2675
			 * @since 1.9.0
2676
			 *
2677
			 * @param string $module Module slug.
2678
			 */
2679
			do_action( 'jetpack_activate_module', $module );
2680
			$active[] = $module;
2681
			$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2682 View Code Duplication
			if ( $active_state = Jetpack::state( $state ) ) {
2683
				$active_state = explode( ',', $active_state );
2684
			} else {
2685
				$active_state = array();
2686
			}
2687
			$active_state[] = $module;
2688
			Jetpack::state( $state, implode( ',', $active_state ) );
2689
			Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2690
			ob_end_clean();
2691
		}
2692
		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...
2693
		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...
2694
		Jetpack::catch_errors( false );
2695
		/**
2696
		 * Fires when default modules are activated.
2697
		 *
2698
		 * @since 1.9.0
2699
		 *
2700
		 * @param string $min_version Minimum version number required to use modules.
2701
		 * @param string $max_version Maximum version number required to use modules.
2702
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2703
		 */
2704
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2705
	}
2706
2707
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2708
		/**
2709
		 * Fires before a module is activated.
2710
		 *
2711
		 * @since 2.6.0
2712
		 *
2713
		 * @param string $module Module slug.
2714
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2715
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2716
		 */
2717
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2718
2719
		$jetpack = Jetpack::init();
2720
2721
		if ( ! strlen( $module ) )
2722
			return false;
2723
2724
		if ( ! Jetpack::is_module( $module ) )
2725
			return false;
2726
2727
		// If it's already active, then don't do it again
2728
		$active = Jetpack::get_active_modules();
2729
		foreach ( $active as $act ) {
2730
			if ( $act == $module )
2731
				return true;
2732
		}
2733
2734
		$module_data = Jetpack::get_module( $module );
2735
2736
		if ( ! Jetpack::is_active() ) {
2737
			if ( !Jetpack::is_development_mode() )
2738
				return false;
2739
2740
			// If we're not connected but in development mode, make sure the module doesn't require a connection
2741
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
2742
				return false;
2743
		}
2744
2745
		// Check and see if the old plugin is active
2746
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2747
			// Deactivate the old plugin
2748
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
2749
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
2750
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
2751
				Jetpack::state( 'deactivated_plugins', $module );
2752
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
2753
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method activate_module() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2754
			}
2755
		}
2756
2757
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
2758
		Jetpack::state( 'module', $module );
2759
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
2760
2761
		Jetpack::catch_errors( true );
2762
		ob_start();
2763
		require Jetpack::get_module_path( $module );
2764
		/** This action is documented in class.jetpack.php */
2765
		do_action( 'jetpack_activate_module', $module );
2766
		$active[] = $module;
2767
		Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2768
		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...
2769
		Jetpack::state( 'message', 'module_activated' );
2770
		Jetpack::state( 'module', $module );
2771
		ob_end_clean();
2772
		Jetpack::catch_errors( false );
2773
2774
		// A flag for Jump Start so it's not shown again. Only set if it hasn't been yet.
2775 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2776
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2777
2778
			//Jump start is being dismissed send data to MC Stats
2779
			$jetpack->stat( 'jumpstart', 'manual,'.$module );
2780
2781
			$jetpack->do_stats( 'server_side' );
2782
		}
2783
2784
		if ( $redirect ) {
2785
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2786
		}
2787
		if ( $exit ) {
2788
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method activate_module() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2789
		}
2790
	}
2791
2792
	function activate_module_actions( $module ) {
2793
		/**
2794
		 * Fires when a module is activated.
2795
		 * The dynamic part of the filter, $module, is the module slug.
2796
		 *
2797
		 * @since 1.9.0
2798
		 *
2799
		 * @param string $module Module slug.
2800
		 */
2801
		do_action( "jetpack_activate_module_$module", $module );
2802
2803
		$this->sync->sync_all_module_options( $module );
2804
	}
2805
2806
	public static function deactivate_module( $module ) {
2807
		/**
2808
		 * Fires when a module is deactivated.
2809
		 *
2810
		 * @since 1.9.0
2811
		 *
2812
		 * @param string $module Module slug.
2813
		 */
2814
		do_action( 'jetpack_pre_deactivate_module', $module );
2815
2816
		$jetpack = Jetpack::init();
2817
2818
		$active = Jetpack::get_active_modules();
2819
		$new    = array_filter( array_diff( $active, (array) $module ) );
2820
2821
		/**
2822
		 * Fires when a module is deactivated.
2823
		 * The dynamic part of the filter, $module, is the module slug.
2824
		 *
2825
		 * @since 1.9.0
2826
		 *
2827
		 * @param string $module Module slug.
2828
		 */
2829
		do_action( "jetpack_deactivate_module_$module", $module );
2830
2831
		// A flag for Jump Start so it's not shown again.
2832 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2833
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2834
2835
			//Jump start is being dismissed send data to MC Stats
2836
			$jetpack->stat( 'jumpstart', 'manual,deactivated-'.$module );
2837
2838
			$jetpack->do_stats( 'server_side' );
2839
		}
2840
2841
		return Jetpack_Options::update_option( 'active_modules', array_unique( $new ) );
2842
	}
2843
2844
	public static function enable_module_configurable( $module ) {
2845
		$module = Jetpack::get_module_slug( $module );
2846
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
2847
	}
2848
2849
	public static function module_configuration_url( $module ) {
2850
		$module = Jetpack::get_module_slug( $module );
2851
		return Jetpack::admin_url( array( 'page' => 'jetpack', 'configure' => $module ) );
2852
	}
2853
2854
	public static function module_configuration_load( $module, $method ) {
2855
		$module = Jetpack::get_module_slug( $module );
2856
		add_action( 'jetpack_module_configuration_load_' . $module, $method );
2857
	}
2858
2859
	public static function module_configuration_head( $module, $method ) {
2860
		$module = Jetpack::get_module_slug( $module );
2861
		add_action( 'jetpack_module_configuration_head_' . $module, $method );
2862
	}
2863
2864
	public static function module_configuration_screen( $module, $method ) {
2865
		$module = Jetpack::get_module_slug( $module );
2866
		add_action( 'jetpack_module_configuration_screen_' . $module, $method );
2867
	}
2868
2869
	public static function module_configuration_activation_screen( $module, $method ) {
2870
		$module = Jetpack::get_module_slug( $module );
2871
		add_action( 'display_activate_module_setting_' . $module, $method );
2872
	}
2873
2874
/* Installation */
2875
2876
	public static function bail_on_activation( $message, $deactivate = true ) {
2877
?>
2878
<!doctype html>
2879
<html>
2880
<head>
2881
<meta charset="<?php bloginfo( 'charset' ); ?>">
2882
<style>
2883
* {
2884
	text-align: center;
2885
	margin: 0;
2886
	padding: 0;
2887
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
2888
}
2889
p {
2890
	margin-top: 1em;
2891
	font-size: 18px;
2892
}
2893
</style>
2894
<body>
2895
<p><?php echo esc_html( $message ); ?></p>
2896
</body>
2897
</html>
2898
<?php
2899
		if ( $deactivate ) {
2900
			$plugins = get_option( 'active_plugins' );
2901
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
2902
			$update  = false;
2903
			foreach ( $plugins as $i => $plugin ) {
2904
				if ( $plugin === $jetpack ) {
2905
					$plugins[$i] = false;
2906
					$update = true;
2907
				}
2908
			}
2909
2910
			if ( $update ) {
2911
				update_option( 'active_plugins', array_filter( $plugins ) );
2912
			}
2913
		}
2914
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method bail_on_activation() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2915
	}
2916
2917
	/**
2918
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
2919
	 * @static
2920
	 */
2921
	public static function plugin_activation( $network_wide ) {
2922
		Jetpack_Options::update_option( 'activated', 1 );
2923
2924
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
2925
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
2926
		}
2927
2928
		if ( $network_wide )
2929
			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...
2930
2931
		Jetpack::plugin_initialize();
2932
	}
2933
	/**
2934
	 * Runs before bumping version numbers up to a new version
2935
	 * @param  (string) $version    Version:timestamp
2936
	 * @param  (string) $old_version Old Version:timestamp or false if not set yet.
2937
	 * @return null              [description]
2938
	 */
2939
	public static function do_version_bump( $version, $old_version ) {
2940
2941
		if ( ! $old_version ) { // For new sites
2942
			// Setting up jetpack manage
2943
			Jetpack::activate_manage();
2944
		}
2945
	}
2946
2947
	/**
2948
	 * Sets the internal version number and activation state.
2949
	 * @static
2950
	 */
2951
	public static function plugin_initialize() {
2952
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
2953
			Jetpack_Options::update_option( 'activated', 2 );
2954
		}
2955
2956 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
2957
			$version = $old_version = JETPACK__VERSION . ':' . time();
2958
			/** This action is documented in class.jetpack.php */
2959
			do_action( 'updating_jetpack_version', $version, false );
2960
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2961
		}
2962
2963
		Jetpack::load_modules();
2964
2965
		Jetpack_Options::delete_option( 'do_activate' );
2966
	}
2967
2968
	/**
2969
	 * Removes all connection options
2970
	 * @static
2971
	 */
2972
	public static function plugin_deactivation( ) {
2973
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
2974
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
2975
			Jetpack_Network::init()->deactivate();
2976
		} else {
2977
			Jetpack::disconnect( false );
2978
			//Jetpack_Heartbeat::init()->deactivate();
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2979
		}
2980
	}
2981
2982
	/**
2983
	 * Disconnects from the Jetpack servers.
2984
	 * Forgets all connection details and tells the Jetpack servers to do the same.
2985
	 * @static
2986
	 */
2987
	public static function disconnect( $update_activated_state = true ) {
2988
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
2989
		Jetpack::clean_nonces( true );
2990
2991
		Jetpack::load_xml_rpc_client();
2992
		$xml = new Jetpack_IXR_Client();
2993
		$xml->query( 'jetpack.deregister' );
2994
2995
		Jetpack_Options::delete_option(
2996
			array(
2997
				'register',
2998
				'blog_token',
2999
				'user_token',
3000
				'user_tokens',
3001
				'master_user',
3002
				'time_diff',
3003
				'fallback_no_verify_ssl_certs',
3004
			)
3005
		);
3006
3007
		if ( $update_activated_state ) {
3008
			Jetpack_Options::update_option( 'activated', 4 );
3009
		}
3010
3011
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
3012
		// Check then record unique disconnection if site has never been disconnected previously
3013
		if ( -1 == $jetpack_unique_connection['disconnected'] ) {
3014
			$jetpack_unique_connection['disconnected'] = 1;
3015
		}
3016
		else {
3017
			if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3018
				//track unique disconnect
3019
				$jetpack = Jetpack::init();
3020
3021
				$jetpack->stat( 'connections', 'unique-disconnect' );
3022
				$jetpack->do_stats( 'server_side' );
3023
			}
3024
			// increment number of times disconnected
3025
			$jetpack_unique_connection['disconnected'] += 1;
3026
		}
3027
3028
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3029
3030
		// Disable the Heartbeat cron
3031
		Jetpack_Heartbeat::init()->deactivate();
3032
	}
3033
3034
	/**
3035
	 * Unlinks the current user from the linked WordPress.com user
3036
	 */
3037
	public static function unlink_user( $user_id = null ) {
3038
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
3039
			return false;
3040
3041
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
3042
3043
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
3044
			return false;
3045
3046
		if ( ! isset( $tokens[ $user_id ] ) )
3047
			return false;
3048
3049
		Jetpack::load_xml_rpc_client();
3050
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
3051
		$xml->query( 'jetpack.unlink_user', $user_id );
3052
3053
		unset( $tokens[ $user_id ] );
3054
3055
		Jetpack_Options::update_option( 'user_tokens', $tokens );
3056
3057
		return true;
3058
	}
3059
3060
	/**
3061
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3062
	 */
3063
	public static function try_registration() {
3064
		// Let's get some testing in beta versions and such.
3065
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3066
			// Before attempting to connect, let's make sure that the domains are viable.
3067
			$domains_to_check = array_unique( array(
3068
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
3069
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
3070
			) );
3071
			foreach ( $domains_to_check as $domain ) {
3072
				$result = Jetpack_Data::is_usable_domain( $domain );
3073
				if ( is_wp_error( $result ) ) {
3074
					return $result;
3075
				}
3076
			}
3077
		}
3078
3079
		$result = Jetpack::register();
3080
3081
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3082
		if ( ! $result || is_wp_error( $result ) ) {
3083
			return $result;
3084
		} else {
3085
			return true;
3086
		}
3087
	}
3088
3089
	/**
3090
	 * Tracking an internal event log. Try not to put too much chaff in here.
3091
	 *
3092
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3093
	 */
3094
	public static function log( $code, $data = null ) {
3095
		// only grab the latest 200 entries
3096
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3097
3098
		// Append our event to the log
3099
		$log_entry = array(
3100
			'time'    => time(),
3101
			'user_id' => get_current_user_id(),
3102
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3103
			'code'    => $code,
3104
		);
3105
		// Don't bother storing it unless we've got some.
3106
		if ( ! is_null( $data ) ) {
3107
			$log_entry['data'] = $data;
3108
		}
3109
		$log[] = $log_entry;
3110
3111
		// Try add_option first, to make sure it's not autoloaded.
3112
		// @todo: Add an add_option method to Jetpack_Options
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
3113
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3114
			Jetpack_Options::update_option( 'log', $log );
3115
		}
3116
3117
		/**
3118
		 * Fires when Jetpack logs an internal event.
3119
		 *
3120
		 * @since 3.0.0
3121
		 *
3122
		 * @param array $log_entry {
3123
		 *	Array of details about the log entry.
3124
		 *
3125
		 *	@param string time Time of the event.
3126
		 *	@param int user_id ID of the user who trigerred the event.
3127
		 *	@param int blog_id Jetpack Blog ID.
3128
		 *	@param string code Unique name for the event.
3129
		 *	@param string data Data about the event.
3130
		 * }
3131
		 */
3132
		do_action( 'jetpack_log_entry', $log_entry );
3133
	}
3134
3135
	/**
3136
	 * Get the internal event log.
3137
	 *
3138
	 * @param $event (string) - only return the specific log events
3139
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3140
	 *
3141
	 * @return array of log events || WP_Error for invalid params
3142
	 */
3143
	public static function get_log( $event = false, $num = false ) {
3144
		if ( $event && ! is_string( $event ) ) {
3145
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
3146
		}
3147
3148
		if ( $num && ! is_numeric( $num ) ) {
3149
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
3150
		}
3151
3152
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3153
3154
		// If nothing set - act as it did before, otherwise let's start customizing the output
3155
		if ( ! $num && ! $event ) {
3156
			return $entire_log;
3157
		} else {
3158
			$entire_log = array_reverse( $entire_log );
3159
		}
3160
3161
		$custom_log_output = array();
3162
3163
		if ( $event ) {
3164
			foreach ( $entire_log as $log_event ) {
3165
				if ( $event == $log_event[ 'code' ] ) {
3166
					$custom_log_output[] = $log_event;
3167
				}
3168
			}
3169
		} else {
3170
			$custom_log_output = $entire_log;
3171
		}
3172
3173
		if ( $num ) {
3174
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3175
		}
3176
3177
		return $custom_log_output;
3178
	}
3179
3180
	/**
3181
	 * Log modification of important settings.
3182
	 */
3183
	public static function log_settings_change( $option, $old_value, $value ) {
3184
		switch( $option ) {
3185
			case 'jetpack_sync_non_public_post_stati':
3186
				self::log( $option, $value );
3187
				break;
3188
		}
3189
	}
3190
3191
	/**
3192
	 * Return stat data for WPCOM sync
3193
	 */
3194
	function get_stat_data() {
3195
		$heartbeat_data = Jetpack_Heartbeat::generate_stats_array();
3196
		$additional_data = $this->get_additional_stat_data();
3197
3198
		return json_encode( array_merge( $heartbeat_data, $additional_data ) );
3199
	}
3200
3201
	/**
3202
	 * Get additional stat data to sync to WPCOM
3203
	 */
3204
	function get_additional_stat_data( $prefix = '' ) {
3205
		$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...
3206
		$return["{$prefix}plugins-extra"]  = Jetpack::get_parsed_plugin_data();
3207
		$return["{$prefix}users"]          = count_users();
3208
		$return["{$prefix}site-count"]     = 0;
3209
		if ( function_exists( 'get_blog_count' ) ) {
3210
			$return["{$prefix}site-count"] = get_blog_count();
3211
		}
3212
		return $return;
3213
	}
3214
3215
	/* Admin Pages */
3216
3217
	function admin_init() {
3218
		// If the plugin is not connected, display a connect message.
3219
		if (
3220
			// the plugin was auto-activated and needs its candy
3221
			Jetpack_Options::get_option( 'do_activate' )
3222
		||
3223
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3224
			! Jetpack_Options::get_option( 'activated' )
3225
		) {
3226
			Jetpack::plugin_initialize();
3227
		}
3228
3229
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
3230
			if ( 4 != Jetpack_Options::get_option( 'activated' ) ) {
3231
				// Show connect notice on dashboard and plugins pages
3232
				add_action( 'load-index.php', array( $this, 'prepare_connect_notice' ) );
3233
				add_action( 'load-plugins.php', array( $this, 'prepare_connect_notice' ) );
3234
			}
3235
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3236
			// Upgrade: 1.1 -> 1.1.1
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3237
			// Check and see if host can verify the Jetpack servers' SSL certificate
3238
			$args = array();
3239
			Jetpack_Client::_wp_remote_request(
3240
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
3241
				$args,
3242
				true
3243
			);
3244
		} else {
3245
			// Show the notice on the Dashboard only for now
3246
3247
			add_action( 'load-index.php', array( $this, 'prepare_manage_jetpack_notice' ) );
3248
3249
			// Identity crisis notices
3250
			add_action( 'jetpack_notices', array( $this, 'alert_identity_crisis' ) );
3251
		}
3252
3253
		// If the plugin has just been disconnected from WP.com, show the survey notice
3254
		if ( isset( $_GET['disconnected'] ) && 'true' === $_GET['disconnected'] ) {
3255
			add_action( 'jetpack_notices', array( $this, 'disconnect_survey_notice' ) );
3256
		}
3257
3258
		if ( current_user_can( 'manage_options' ) && 'ALWAYS' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3259
			add_action( 'admin_notices', array( $this, 'alert_required_ssl_fail' ) );
3260
		}
3261
3262
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3263
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3264
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3265
3266
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
3267
			// Artificially throw errors in certain whitelisted cases during plugin activation
3268
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3269
3270
			// Kick off synchronization of user role when it changes
3271
			add_action( 'set_user_role', array( $this, 'user_role_change' ) );
3272
		}
3273
3274
		// Jetpack Manage Activation Screen from .com
3275
		Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
3276
	}
3277
3278
	function admin_body_class( $admin_body_class = '' ) {
3279
		$classes = explode( ' ', trim( $admin_body_class ) );
3280
3281
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3282
3283
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3284
		return " $admin_body_class ";
3285
	}
3286
3287
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3288
		return $admin_body_class . ' jetpack-pagestyles ';
3289
	}
3290
3291
	function prepare_connect_notice() {
3292
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3293
3294
		add_action( 'admin_notices', array( $this, 'admin_connect_notice' ) );
3295
3296
		if ( Jetpack::state( 'network_nag' ) )
3297
			add_action( 'network_admin_notices', array( $this, 'network_connect_notice' ) );
3298
	}
3299
	/**
3300
	 * Call this function if you want the Big Jetpack Manage Notice to show up.
3301
	 *
3302
	 * @return null
3303
	 */
3304
	function prepare_manage_jetpack_notice() {
3305
3306
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3307
		add_action( 'admin_notices', array( $this, 'admin_jetpack_manage_notice' ) );
3308
	}
3309
3310
	function manage_activate_screen() {
3311
		include ( JETPACK__PLUGIN_DIR . 'modules/manage/activate-admin.php' );
3312
	}
3313
	/**
3314
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3315
	 * This function artificially throws errors for such cases (whitelisted).
3316
	 *
3317
	 * @param string $plugin The activated plugin.
3318
	 */
3319
	function throw_error_on_activate_plugin( $plugin ) {
3320
		$active_modules = Jetpack::get_active_modules();
3321
3322
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3323
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3324
			$throw = false;
3325
3326
			// Try and make sure it really was the stats plugin
3327
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3328
				if ( 'stats.php' == basename( $plugin ) ) {
3329
					$throw = true;
3330
				}
3331
			} else {
3332
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3333
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3334
					$throw = true;
3335
				}
3336
			}
3337
3338
			if ( $throw ) {
3339
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3340
			}
3341
		}
3342
	}
3343
3344
	function intercept_plugin_error_scrape_init() {
3345
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3346
	}
3347
3348
	function intercept_plugin_error_scrape( $action, $result ) {
3349
		if ( ! $result ) {
3350
			return;
3351
		}
3352
3353
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3354
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3355
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3356
			}
3357
		}
3358
	}
3359
3360
	function add_remote_request_handlers() {
3361
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
3362
	}
3363
3364
	function remote_request_handlers() {
3365
		switch ( current_filter() ) {
3366
		case 'wp_ajax_nopriv_jetpack_upload_file' :
3367
			$response = $this->upload_handler();
3368
			break;
3369
		default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
3370
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
3371
			break;
3372
		}
3373
3374
		if ( ! $response ) {
3375
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
3376
		}
3377
3378
		if ( is_wp_error( $response ) ) {
3379
			$status_code       = $response->get_error_data();
3380
			$error             = $response->get_error_code();
3381
			$error_description = $response->get_error_message();
3382
3383
			if ( ! is_int( $status_code ) ) {
3384
				$status_code = 400;
3385
			}
3386
3387
			status_header( $status_code );
3388
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
0 ignored issues
show
Coding Style Compatibility introduced by
The method remote_request_handlers() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3389
		}
3390
3391
		status_header( 200 );
3392
		if ( true === $response ) {
3393
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method remote_request_handlers() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3394
		}
3395
3396
		die( json_encode( (object) $response ) );
0 ignored issues
show
Coding Style Compatibility introduced by
The method remote_request_handlers() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3397
	}
3398
3399
	function upload_handler() {
3400
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3401
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
3402
		}
3403
3404
		$user = wp_authenticate( '', '' );
3405
		if ( ! $user || is_wp_error( $user ) ) {
3406
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
3407
		}
3408
3409
		wp_set_current_user( $user->ID );
3410
3411
		if ( ! current_user_can( 'upload_files' ) ) {
3412
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3413
		}
3414
3415
		if ( empty( $_FILES ) ) {
3416
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3417
		}
3418
3419
		foreach ( array_keys( $_FILES ) as $files_key ) {
3420
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3421
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3422
			}
3423
		}
3424
3425
		$media_keys = array_keys( $_FILES['media'] );
3426
3427
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
3428
		if ( ! $token || is_wp_error( $token ) ) {
3429
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3430
		}
3431
3432
		$uploaded_files = array();
3433
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3434
		unset( $GLOBALS['post'] );
3435
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3436
			$file = array();
3437
			foreach ( $media_keys as $media_key ) {
3438
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3439
			}
3440
3441
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3442
3443
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3444
			if ( $hmac_provided !== $hmac_file ) {
3445
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3446
				continue;
3447
			}
3448
3449
			$_FILES['.jetpack.upload.'] = $file;
3450
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3451
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3452
				$post_id = 0;
3453
			}
3454
			$attachment_id = media_handle_upload(
3455
				'.jetpack.upload.',
3456
				$post_id,
3457
				array(),
3458
				array(
3459
					'action' => 'jetpack_upload_file',
3460
				)
3461
			);
3462
3463
			if ( ! $attachment_id ) {
3464
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3465
			} elseif ( is_wp_error( $attachment_id ) ) {
3466
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3467
			} else {
3468
				$attachment = get_post( $attachment_id );
3469
				$uploaded_files[$index] = (object) array(
3470
					'id'   => (string) $attachment_id,
3471
					'file' => $attachment->post_title,
3472
					'url'  => wp_get_attachment_url( $attachment_id ),
3473
					'type' => $attachment->post_mime_type,
3474
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3475
				);
3476
			}
3477
		}
3478
		if ( ! is_null( $global_post ) ) {
3479
			$GLOBALS['post'] = $global_post;
3480
		}
3481
3482
		return $uploaded_files;
3483
	}
3484
3485
	/**
3486
	 * Add help to the Jetpack page
3487
	 *
3488
	 * @since Jetpack (1.2.3)
3489
	 * @return false if not the Jetpack page
3490
	 */
3491
	function admin_help() {
3492
		$current_screen = get_current_screen();
3493
3494
		// Overview
3495
		$current_screen->add_help_tab(
3496
			array(
3497
				'id'		=> 'home',
3498
				'title'		=> __( 'Home', 'jetpack' ),
3499
				'content'	=>
3500
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3501
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3502
					'<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>',
3503
			)
3504
		);
3505
3506
		// Screen Content
3507
		if ( current_user_can( 'manage_options' ) ) {
3508
			$current_screen->add_help_tab(
3509
				array(
3510
					'id'		=> 'settings',
3511
					'title'		=> __( 'Settings', 'jetpack' ),
3512
					'content'	=>
3513
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3514
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3515
						'<ol>' .
3516
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3517
							'<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>' .
3518
						'</ol>' .
3519
						'<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>'
3520
				)
3521
			);
3522
		}
3523
3524
		// Help Sidebar
3525
		$current_screen->set_help_sidebar(
3526
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3527
			'<p><a href="http://jetpack.me/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
3528
			'<p><a href="http://jetpack.me/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3529
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3530
		);
3531
	}
3532
3533
	function admin_menu_css() {
3534
		wp_enqueue_style( 'jetpack-icons' );
3535
	}
3536
3537
	function admin_menu_order() {
3538
		return true;
3539
	}
3540
3541 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3542
		$jp_menu_order = array();
3543
3544
		foreach ( $menu_order as $index => $item ) {
3545
			if ( $item != 'jetpack' ) {
3546
				$jp_menu_order[] = $item;
3547
			}
3548
3549
			if ( $index == 0 ) {
3550
				$jp_menu_order[] = 'jetpack';
3551
			}
3552
		}
3553
3554
		return $jp_menu_order;
3555
	}
3556
3557
	function admin_head() {
3558 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) )
3559
			/** This action is documented in class.jetpack-admin-page.php */
3560
			do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
3561
	}
3562
3563
	function admin_banner_styles() {
3564
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3565
3566
		wp_enqueue_style( 'jetpack', plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION . '-20121016' );
3567
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3568
		wp_style_add_data( 'jetpack', 'suffix', $min );
3569
	}
3570
3571
	function admin_scripts() {
3572
		wp_enqueue_script( 'jetpack-js', plugins_url( '_inc/jp.js', JETPACK__PLUGIN_FILE ), array( 'jquery', 'wp-util' ), JETPACK__VERSION . '-20121111' );
3573
		wp_localize_script(
3574
			'jetpack-js',
3575
			'jetpackL10n',
3576
			array(
3577
				'ays_disconnect' => "This will deactivate all Jetpack modules.\nAre you sure you want to disconnect?",
3578
				'ays_unlink'     => "This will prevent user-specific modules such as Publicize, Notifications and Post By Email from working.\nAre you sure you want to unlink?",
3579
				'ays_dismiss'    => "This will deactivate Jetpack.\nAre you sure you want to deactivate Jetpack?",
3580
			)
3581
		);
3582
		add_action( 'admin_footer', array( $this, 'do_stats' ) );
3583
	}
3584
3585
	function plugin_action_links( $actions ) {
3586
3587
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), __( 'Jetpack', 'jetpack' ) ) );
3588
3589
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
3590
			return array_merge(
3591
				$jetpack_home,
3592
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack_modules' ), __( 'Settings', 'jetpack' ) ) ),
3593
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
3594
				$actions
3595
				);
3596
			}
3597
3598
		return array_merge( $jetpack_home, $actions );
3599
	}
3600
3601
	function admin_connect_notice() {
3602
		// Don't show the connect notice anywhere but the plugins.php after activating
3603
		$current = get_current_screen();
3604
		if ( 'plugins' !== $current->parent_base )
3605
			return;
3606
3607
		if ( ! current_user_can( 'jetpack_connect' ) )
3608
			return;
3609
3610
		$dismiss_and_deactivate_url = wp_nonce_url( Jetpack::admin_url( '?page=jetpack&jetpack-notice=dismiss' ), 'jetpack-deactivate' );
3611
		?>
3612
		<div id="message" class="updated jetpack-message jp-banner" style="display:block !important;">
3613
			<a class="jp-banner__dismiss" href="<?php echo esc_url( $dismiss_and_deactivate_url ); ?>" title="<?php esc_attr_e( 'Dismiss this notice and deactivate Jetpack.', 'jetpack' ); ?>"></a>
3614
			<?php if ( in_array( Jetpack_Options::get_option( 'activated' ) , array( 1, 2, 3 ) ) ) : ?>
3615
				<div class="jp-banner__content is-connection">
3616
					<h2><?php _e( 'Your Jetpack is almost ready!', 'jetpack' ); ?></h2>
3617
					<p><?php _e( 'Connect now to enable features like Stats, Likes, and Social Sharing.', 'jetpack' ); ?></p>
3618
				</div>
3619
				<div class="jp-banner__action-container is-connection">
3620
						<a href="<?php echo $this->build_connect_url() ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Connect to WordPress.com', 'jetpack' ); ?></a>
3621
				</div>
3622 View Code Duplication
			<?php else : ?>
3623
				<div class="jp-banner__content">
3624
					<h2><?php _e( 'Jetpack is installed!', 'jetpack' ) ?></h2>
3625
					<p><?php _e( 'It\'s ready to bring awesome, WordPress.com cloud-powered features to your site.', 'jetpack' ) ?></p>
3626
				</div>
3627
				<div class="jp-banner__action-container">
3628
					<a href="<?php echo Jetpack::admin_url() ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Learn More', 'jetpack' ); ?></a>
3629
				</div>
3630
			<?php endif; ?>
3631
		</div>
3632
3633
		<?php
3634
	}
3635
3636
	/**
3637
	 * This is the first banner
3638
	 * It should be visible only to user that can update the option
3639
	 * Are not connected
3640
	 *
3641
	 * @return null
3642
	 */
3643
	function admin_jetpack_manage_notice() {
3644
		$screen = get_current_screen();
3645
3646
		// Don't show the connect notice on the jetpack settings page.
3647
		if ( ! in_array( $screen->base, array( 'dashboard' ) ) || $screen->is_network || $screen->action )
3648
			return;
3649
3650
		// Only show it if don't have the managment option set.
3651
		// And not dismissed it already.
3652
		if ( ! $this->can_display_jetpack_manage_notice() || Jetpack_Options::get_option( 'dismissed_manage_banner' ) ) {
3653
			return;
3654
		}
3655
3656
		$opt_out_url = $this->opt_out_jetpack_manage_url();
3657
		$opt_in_url  = $this->opt_in_jetpack_manage_url();
3658
		/**
3659
		 * I think it would be great to have different wordsing depending on where you are
3660
		 * for example if we show the notice on dashboard and a different one if we show it on Plugins screen
3661
		 * etc..
3662
		 */
3663
3664
		?>
3665
		<div id="message" class="updated jetpack-message jp-banner is-opt-in" style="display:block !important;">
3666
			<a class="jp-banner__dismiss" href="<?php echo esc_url( $opt_out_url ); ?>" title="<?php esc_attr_e( 'Dismiss this notice for now.', 'jetpack' ); ?>"></a>
3667
			<div class="jp-banner__content">
3668
				<h2><?php esc_html_e( 'New in Jetpack: Centralized Site Management', 'jetpack' ); ?></h2>
3669
				<p><?php printf( __( 'Manage multiple sites from one dashboard at wordpress.com/sites. Enabling allows all existing, connected Administrators to modify your site from WordPress.com. <a href="%s" target="_blank">Learn More</a>.', 'jetpack' ), 'http://jetpack.me/support/site-management' ); ?></p>
3670
			</div>
3671
			<div class="jp-banner__action-container is-opt-in">
3672
				<a href="<?php echo esc_url( $opt_in_url ); ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Activate now', 'jetpack' ); ?></a>
3673
			</div>
3674
		</div>
3675
		<?php
3676
	}
3677
3678
	/**
3679
	 * Returns the url that the user clicks to remove the notice for the big banner
3680
	 * @return (string)
3681
	 */
3682
	function opt_out_jetpack_manage_url() {
3683
		$referer = '&_wp_http_referer=' . add_query_arg( '_wp_http_referer', null );
3684
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-out' . $referer ), 'jetpack_manage_banner_opt_out' );
3685
	}
3686
	/**
3687
	 * Returns the url that the user clicks to opt in to Jetpack Manage
3688
	 * @return (string)
3689
	 */
3690
	function opt_in_jetpack_manage_url() {
3691
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-in' ), 'jetpack_manage_banner_opt_in' );
3692
	}
3693
3694
	function opt_in_jetpack_manage_notice() {
3695
		?>
3696
		<div class="wrap">
3697
			<div id="message" class="jetpack-message is-opt-in">
3698
				<?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(), 'http://jetpack.me/support/site-management' ); ?>
3699
			</div>
3700
		</div>
3701
		<?php
3702
3703
	}
3704
	/**
3705
	 * Determines whether to show the notice of not true = display notice
3706
	 * @return (bool)
3707
	 */
3708
	function can_display_jetpack_manage_notice() {
3709
		// never display the notice to users that can't do anything about it anyways
3710
		if( ! current_user_can( 'jetpack_manage_modules' ) )
3711
			return false;
3712
3713
		// don't display if we are in development more
3714
		if( Jetpack::is_development_mode() ) {
3715
			return false;
3716
		}
3717
		// don't display if the site is private
3718
		if(  ! Jetpack_Options::get_option( 'public' ) )
3719
			return false;
3720
3721
		/**
3722
		 * Should the Jetpack Remote Site Management notice be displayed.
3723
		 *
3724
		 * @since 3.3.0
3725
		 *
3726
		 * @param bool ! self::is_module_active( 'manage' ) Is the Manage module inactive.
3727
		 */
3728
		return apply_filters( 'can_display_jetpack_manage_notice', ! self::is_module_active( 'manage' ) );
3729
	}
3730
3731
	function network_connect_notice() {
3732
		?>
3733
		<div id="message" class="updated jetpack-message">
3734
			<div class="squeezer">
3735
				<h2><?php _e( '<strong>Jetpack is activated!</strong> Each site on your network must be connected individually by an admin on that site.', 'jetpack' ) ?></h2>
3736
			</div>
3737
		</div>
3738
		<?php
3739
	}
3740
3741
	public static function jetpack_comment_notice() {
3742
		if ( in_array( 'comments', Jetpack::get_active_modules() ) ) {
3743
			return '';
3744
		}
3745
3746
		$jetpack_old_version = explode( ':', Jetpack_Options::get_option( 'old_version' ) );
3747
		$jetpack_new_version = explode( ':', Jetpack_Options::get_option( 'version' ) );
3748
3749
		if ( $jetpack_old_version ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jetpack_old_version 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...
3750
			if ( version_compare( $jetpack_old_version[0], '1.4', '>=' ) ) {
3751
				return '';
3752
			}
3753
		}
3754
3755
		if ( $jetpack_new_version ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jetpack_new_version 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...
3756
			if ( version_compare( $jetpack_new_version[0], '1.4-something', '<' ) ) {
3757
				return '';
3758
			}
3759
		}
3760
3761
		return '<br /><br />' . sprintf(
3762
			__( 'Jetpack now includes Comments, which enables your visitors to use their WordPress.com, Twitter, or Facebook accounts when commenting on your site. To activate Comments, <a href="%s">%s</a>.', 'jetpack' ),
3763
			wp_nonce_url(
3764
				Jetpack::admin_url(
3765
					array(
3766
						'page'   => 'jetpack',
3767
						'action' => 'activate',
3768
						'module' => 'comments',
3769
					)
3770
				),
3771
				'jetpack_activate-comments'
3772
			),
3773
			__( 'click here', 'jetpack' )
3774
		);
3775
	}
3776
3777
	/**
3778
	 * Show the survey link when the user has just disconnected Jetpack.
3779
	 */
3780
	function disconnect_survey_notice() {
3781
		?>
3782
		<div class="wrap">
3783
			<div id="message" class="jetpack-message stay-visible">
3784
				<div class="squeezer">
3785
					<h2>
3786
						<?php _e( 'You have successfully disconnected Jetpack.', 'jetpack' ); ?>
3787
						<br />
3788
						<?php echo sprintf(
3789
							__( 'Would you tell us why? Just <a href="%1$s" target="%2$s">answering two simple questions</a> would help us improve Jetpack.', 'jetpack' ),
3790
							'https://jetpack.me/survey-disconnected/',
3791
							'_blank'
3792
						); ?>
3793
					</h2>
3794
				</div>
3795
			</div>
3796
		</div>
3797
		<?php
3798
	}
3799
3800
	/*
3801
	 * Registration flow:
3802
	 * 1 - ::admin_page_load() action=register
3803
	 * 2 - ::try_registration()
3804
	 * 3 - ::register()
3805
	 *     - Creates jetpack_register option containing two secrets and a timestamp
3806
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
3807
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
3808
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
3809
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
3810
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
3811
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
3812
	 *       jetpack_id, jetpack_secret, jetpack_public
3813
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
3814
	 * 4 - redirect to https://jetpack.wordpress.com/jetpack.authorize/1/
3815
	 * 5 - user logs in with WP.com account
3816
	 * 6 - redirect to this site's wp-admin/index.php?page=jetpack&action=authorize with
3817
	 *     code <-- OAuth2 style authorization code
3818
	 * 7 - ::admin_page_load() action=authorize
3819
	 * 8 - Jetpack_Client_Server::authorize()
3820
	 * 9 - Jetpack_Client_Server::get_token()
3821
	 * 10- GET https://jetpack.wordpress.com/jetpack.token/1/ with
3822
	 *     client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
3823
	 * 11- which responds with
3824
	 *     access_token, token_type, scope
3825
	 * 12- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
3826
	 * 13- Jetpack::activate_default_modules()
3827
	 *     Deactivates deprecated plugins
3828
	 *     Activates all default modules
3829
	 *     Catches errors: redirects to wp-admin/index.php?page=jetpack state:error=something
3830
	 * 14- redirect to this site's wp-admin/index.php?page=jetpack with state:message=authorized
3831
	 *     Done!
3832
	 */
3833
3834
	/**
3835
	 * Handles the page load events for the Jetpack admin page
3836
	 */
3837
	function admin_page_load() {
3838
		$error = false;
3839
3840
		// Make sure we have the right body class to hook stylings for subpages off of.
3841
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
3842
3843
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
3844
			// Should only be used in intermediate redirects to preserve state across redirects
3845
			Jetpack::restate();
3846
		}
3847
3848
		if ( isset( $_GET['connect_url_redirect'] ) ) {
3849
			// User clicked in the iframe to link their accounts
3850
			if ( ! Jetpack::is_user_connected() ) {
3851
				$connect_url = $this->build_connect_url( true );
3852
				if ( isset( $_GET['notes_iframe'] ) )
3853
					$connect_url .= '&notes_iframe';
3854
				wp_redirect( $connect_url );
3855
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3856
			} else {
3857
				Jetpack::state( 'message', 'already_authorized' );
3858
				wp_safe_redirect( Jetpack::admin_url() );
3859
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3860
			}
3861
		}
3862
3863
3864
		if ( isset( $_GET['action'] ) ) {
3865
			switch ( $_GET['action'] ) {
3866
			case 'authorize' :
3867
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
3868
					Jetpack::state( 'message', 'already_authorized' );
3869
					wp_safe_redirect( Jetpack::admin_url() );
3870
					exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3871
				}
3872
				Jetpack::log( 'authorize' );
3873
				$client_server = new Jetpack_Client_Server;
3874
				$client_server->authorize();
3875
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3876
			case 'register' :
3877
				if ( ! current_user_can( 'jetpack_connect' ) ) {
3878
					$error = 'cheatin';
3879
					break;
3880
				}
3881
				check_admin_referer( 'jetpack-register' );
3882
				Jetpack::log( 'register' );
3883
				Jetpack::maybe_set_version_option();
3884
				$registered = Jetpack::try_registration();
3885
				if ( is_wp_error( $registered ) ) {
3886
					$error = $registered->get_error_code();
3887
					Jetpack::state( 'error_description', $registered->get_error_message() );
3888
					break;
3889
				}
3890
3891
				wp_redirect( $this->build_connect_url( true ) );
3892
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3893
			case 'activate' :
3894
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3895
					$error = 'cheatin';
3896
					break;
3897
				}
3898
3899
				$module = stripslashes( $_GET['module'] );
3900
				check_admin_referer( "jetpack_activate-$module" );
3901
				Jetpack::log( 'activate', $module );
3902
				Jetpack::activate_module( $module );
3903
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3904
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3905
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3906
			case 'activate_default_modules' :
3907
				check_admin_referer( 'activate_default_modules' );
3908
				Jetpack::log( 'activate_default_modules' );
3909
				Jetpack::restate();
3910
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
3911
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
3912
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
3913
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
3914
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3915
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3916
			case 'disconnect' :
3917
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3918
					$error = 'cheatin';
3919
					break;
3920
				}
3921
3922
				check_admin_referer( 'jetpack-disconnect' );
3923
				Jetpack::log( 'disconnect' );
3924
				Jetpack::disconnect();
3925
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
3926
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3927
			case 'reconnect' :
3928
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
3929
					$error = 'cheatin';
3930
					break;
3931
				}
3932
3933
				check_admin_referer( 'jetpack-reconnect' );
3934
				Jetpack::log( 'reconnect' );
3935
				$this->disconnect();
3936
				wp_redirect( $this->build_connect_url( true ) );
3937
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3938 View Code Duplication
			case 'deactivate' :
3939
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
3940
					$error = 'cheatin';
3941
					break;
3942
				}
3943
3944
				$modules = stripslashes( $_GET['module'] );
3945
				check_admin_referer( "jetpack_deactivate-$modules" );
3946
				foreach ( explode( ',', $modules ) as $module ) {
3947
					Jetpack::log( 'deactivate', $module );
3948
					Jetpack::deactivate_module( $module );
3949
					Jetpack::state( 'message', 'module_deactivated' );
3950
				}
3951
				Jetpack::state( 'module', $modules );
3952
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3953
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3954
			case 'unlink' :
3955
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
3956
				check_admin_referer( 'jetpack-unlink' );
3957
				Jetpack::log( 'unlink' );
3958
				$this->unlink_user();
3959
				Jetpack::state( 'message', 'unlinked' );
3960
				if ( 'sub-unlink' == $redirect ) {
3961
					wp_safe_redirect( admin_url() );
3962
				} else {
3963
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
3964
				}
3965
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3966
			default:
3967
				/**
3968
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
3969
				 *
3970
				 * @since 2.6.0
3971
				 *
3972
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
3973
				 */
3974
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
3975
			}
3976
		}
3977
3978
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
3979
			self::activate_new_modules( true );
3980
		}
3981
3982
		switch ( $error ) {
3983
		case 'cheatin' :
3984
			$this->error = __( 'Cheatin&#8217; uh?', 'jetpack' );
3985
			break;
3986
		case 'access_denied' :
3987
			$this->error = __( 'You need to authorize the Jetpack connection between your site and WordPress.com to enable the awesome features.', 'jetpack' );
3988
			break;
3989
		case 'wrong_state' :
3990
			$this->error = __( 'Don&#8217;t cross the streams!  You need to stay logged in to your WordPress blog while you authorize Jetpack.', 'jetpack' );
3991
			break;
3992
		case 'invalid_client' :
3993
			// @todo re-register instead of deactivate/reactivate
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
3994
			$this->error = __( 'Return to sender.  Whoops! It looks like you got the wrong Jetpack in the mail; deactivate then reactivate the Jetpack plugin to get a new one.', 'jetpack' );
3995
			break;
3996
		case 'invalid_grant' :
3997
			$this->error = __( 'Wrong size.  Hm&#8230; it seems your Jetpack doesn&#8217;t quite fit.  Have you lost weight? Click &#8220;Connect to WordPress.com&#8221; again to get your Jetpack adjusted.', 'jetpack' );
3998
			break;
3999
		case 'site_inaccessible' :
4000
		case 'site_requires_authorization' :
4001
			$this->error = sprintf( __( 'Your website needs to be publicly accessible to use Jetpack: %s', 'jetpack' ), "<code>$error</code>" );
4002
			break;
4003
		case 'module_activation_failed' :
4004
			$module = Jetpack::state( 'module' );
4005
			if ( ! empty( $module ) && $mod = Jetpack::get_module( $module ) ) {
4006
				$this->error = sprintf( __( '%s could not be activated because it triggered a <strong>fatal error</strong>. Perhaps there is a conflict with another plugin you have installed?', 'jetpack' ), $mod['name'] );
4007
				if ( isset( $this->plugins_to_deactivate[$module] ) ) {
4008
					$this->error .= ' ' . sprintf( __( 'Do you still have the %s plugin installed?', 'jetpack' ), $this->plugins_to_deactivate[$module][1] );
4009
				}
4010
			} else {
4011
				$this->error = __( 'Module could not be activated because it triggered a <strong>fatal error</strong>. Perhaps there is a conflict with another plugin you have installed?', 'jetpack' );
4012
			}
4013
			if ( $php_errors = Jetpack::state( 'php_errors' ) ) {
4014
				$this->error .= "<br />\n";
4015
				$this->error .= $php_errors;
4016
			}
4017
			break;
4018
		case 'master_user_required' :
4019
			$module = Jetpack::state( 'module' );
4020
			$module_name = '';
4021
			if ( ! empty( $module ) && $mod = Jetpack::get_module( $module ) ) {
4022
				$module_name = $mod['name'];
4023
			}
4024
4025
			$master_user = Jetpack_Options::get_option( 'master_user' );
4026
			$master_userdata = get_userdata( $master_user ) ;
4027
			if ( $master_userdata ) {
4028
				if ( ! in_array( $module, Jetpack::get_active_modules() ) ) {
4029
					$this->error = sprintf( __( '%s was not activated.' , 'jetpack' ), $module_name );
4030
				} else {
4031
					$this->error = sprintf( __( '%s was not deactivated.' , 'jetpack' ), $module_name );
4032
				}
4033
				$this->error .= '  ' . sprintf( __( 'This module can only be altered by %s, the user who initiated the Jetpack connection on this site.' , 'jetpack' ), esc_html( $master_userdata->display_name ) );
4034
4035
			} else {
4036
				$this->error = sprintf( __( 'Only the user who initiated the Jetpack connection on this site can toggle %s, but that user no longer exists. This should not happen.', 'jetpack' ), $module_name );
4037
			}
4038
			break;
4039
		case 'not_public' :
4040
			$this->error = __( '<strong>Your Jetpack has a glitch.</strong> Connecting this site with WordPress.com is not possible. This usually means your site is not publicly accessible (localhost).', 'jetpack' );
4041
			break;
4042
		case 'wpcom_408' :
4043
		case 'wpcom_5??' :
4044
		case 'wpcom_bad_response' :
4045
		case 'wpcom_outage' :
4046
			$this->error = __( 'WordPress.com is currently having problems and is unable to fuel up your Jetpack.  Please try again later.', 'jetpack' );
4047
			break;
4048
		case 'register_http_request_failed' :
4049
		case 'token_http_request_failed' :
4050
			$this->error = sprintf( __( 'Jetpack could not contact WordPress.com: %s.  This usually means something is incorrectly configured on your web host.', 'jetpack' ), "<code>$error</code>" );
4051
			break;
4052
		default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
4053
			if ( empty( $error ) ) {
4054
				break;
4055
			}
4056
			$error = trim( substr( strip_tags( $error ), 0, 20 ) );
4057
			// no break: fall through
4058
		case 'no_role' :
0 ignored issues
show
Unused Code introduced by
// no break: fall through case 'no_role': does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
4059
		case 'no_cap' :
4060
		case 'no_code' :
4061
		case 'no_state' :
4062
		case 'invalid_state' :
4063
		case 'invalid_request' :
4064
		case 'invalid_scope' :
4065
		case 'unsupported_response_type' :
4066
		case 'invalid_token' :
4067
		case 'no_token' :
4068
		case 'missing_secrets' :
4069
		case 'home_missing' :
4070
		case 'siteurl_missing' :
4071
		case 'gmt_offset_missing' :
4072
		case 'site_name_missing' :
4073
		case 'secret_1_missing' :
4074
		case 'secret_2_missing' :
4075
		case 'site_lang_missing' :
4076
		case 'home_malformed' :
4077
		case 'siteurl_malformed' :
4078
		case 'gmt_offset_malformed' :
4079
		case 'timezone_string_malformed' :
4080
		case 'site_name_malformed' :
4081
		case 'secret_1_malformed' :
4082
		case 'secret_2_malformed' :
4083
		case 'site_lang_malformed' :
4084
		case 'secrets_mismatch' :
4085
		case 'verify_secret_1_missing' :
4086
		case 'verify_secret_1_malformed' :
4087
		case 'verify_secrets_missing' :
4088
		case 'verify_secrets_mismatch' :
4089
			$error = esc_html( $error );
4090
			$this->error = sprintf( __( '<strong>Your Jetpack has a glitch.</strong>  Something went wrong that&#8217;s never supposed to happen.  Guess you&#8217;re just lucky: %s', 'jetpack' ), "<code>$error</code>" );
4091
			if ( ! Jetpack::is_active() ) {
4092
				$this->error .= '<br />';
4093
				$this->error .= sprintf( __( 'Try connecting again.', 'jetpack' ) );
4094
			}
4095
			break;
4096
		}
4097
4098
		$message_code = Jetpack::state( 'message' );
4099
4100
		$active_state = Jetpack::state( 'activated_modules' );
4101
		if ( ! empty( $active_state ) ) {
4102
			$available    = Jetpack::get_available_modules();
4103
			$active_state = explode( ',', $active_state );
4104
			$active_state = array_intersect( $active_state, $available );
4105
			if ( count( $active_state ) ) {
4106
				foreach ( $active_state as $mod ) {
4107
					$this->stat( 'module-activated', $mod );
4108
				}
4109
			} else {
4110
				$active_state = false;
4111
			}
4112
		}
4113
		if( Jetpack::state( 'optin-manage' ) ) {
4114
			$activated_manage = $message_code;
4115
			$message_code = 'jetpack-manage';
4116
4117
		}
4118
		switch ( $message_code ) {
4119
		case 'modules_activated' :
4120
			$this->message = sprintf(
4121
				__( 'Welcome to <strong>Jetpack %s</strong>!', 'jetpack' ),
4122
				JETPACK__VERSION
4123
			);
4124
4125
			if ( $active_state ) {
4126
				$titles = array();
4127 View Code Duplication
				foreach ( $active_state as $mod ) {
4128
					if ( $mod_headers = Jetpack::get_module( $mod ) ) {
4129
						$titles[] = '<strong>' . preg_replace( '/\s+(?![^<>]++>)/', '&nbsp;', $mod_headers['name'] ) . '</strong>';
4130
					}
4131
				}
4132
				if ( $titles ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $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...
4133
					$this->message .= '<br /><br />' . wp_sprintf( __( 'The following new modules have been activated: %l.', 'jetpack' ), $titles );
4134
				}
4135
			}
4136
4137
			if ( $reactive_state = Jetpack::state( 'reactivated_modules' ) ) {
4138
				$titles = array();
4139 View Code Duplication
				foreach ( explode( ',',  $reactive_state ) as $mod ) {
4140
					if ( $mod_headers = Jetpack::get_module( $mod ) ) {
4141
						$titles[] = '<strong>' . preg_replace( '/\s+(?![^<>]++>)/', '&nbsp;', $mod_headers['name'] ) . '</strong>';
4142
					}
4143
				}
4144
				if ( $titles ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $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...
4145
					$this->message .= '<br /><br />' . wp_sprintf( __( 'The following modules have been updated: %l.', 'jetpack' ), $titles );
4146
				}
4147
			}
4148
4149
			$this->message .= Jetpack::jetpack_comment_notice();
4150
			break;
4151
		case 'jetpack-manage':
4152
			$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>';
4153
			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...
4154
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
4155
			}
4156
			break;
4157
		case 'module_activated' :
4158
			if ( $module = Jetpack::get_module( Jetpack::state( 'module' ) ) ) {
4159
				$this->message = sprintf( __( '<strong>%s Activated!</strong> You can deactivate at any time by clicking the Deactivate link next to each module.', 'jetpack' ), $module['name'] );
4160
				$this->stat( 'module-activated', Jetpack::state( 'module' ) );
4161
			}
4162
			break;
4163
4164
		case 'module_deactivated' :
4165
			$modules = Jetpack::state( 'module' );
4166
			if ( ! $modules ) {
4167
				break;
4168
			}
4169
4170
			$module_names = array();
4171
			foreach ( explode( ',', $modules ) as $module_slug ) {
4172
				$module = Jetpack::get_module( $module_slug );
4173
				if ( $module ) {
4174
					$module_names[] = $module['name'];
4175
				}
4176
4177
				$this->stat( 'module-deactivated', $module_slug );
4178
			}
4179
4180
			if ( ! $module_names ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $module_names 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...
4181
				break;
4182
			}
4183
4184
			$this->message = wp_sprintf(
4185
				_nx(
4186
					'<strong>%l Deactivated!</strong> You can activate it again at any time using the activate link next to each module.',
4187
					'<strong>%l Deactivated!</strong> You can activate them again at any time using the activate links next to each module.',
4188
					count( $module_names ),
4189
					'%l = list of Jetpack module/feature names',
4190
					'jetpack'
4191
				),
4192
				$module_names
4193
			);
4194
			break;
4195
4196
		case 'module_configured' :
4197
			$this->message = __( '<strong>Module settings were saved.</strong> ', 'jetpack' );
4198
			break;
4199
4200
		case 'already_authorized' :
4201
			$this->message = __( '<strong>Your Jetpack is already connected.</strong> ', 'jetpack' );
4202
			break;
4203
4204
		case 'authorized' :
4205
			$this->message  = __( '<strong>You&#8217;re fueled up and ready to go, Jetpack is now active.</strong> ', 'jetpack' );
4206
			$this->message .= Jetpack::jetpack_comment_notice();
4207
			break;
4208
4209
		case 'linked' :
4210
			$this->message  = __( '<strong>You&#8217;re fueled up and ready to go.</strong> ', 'jetpack' );
4211
			$this->message .= Jetpack::jetpack_comment_notice();
4212
			break;
4213
4214
		case 'unlinked' :
4215
			$user = wp_get_current_user();
4216
			$this->message = sprintf( __( '<strong>You have unlinked your account (%s) from WordPress.com.</strong>', 'jetpack' ), $user->user_login );
4217
			break;
4218
4219
		case 'switch_master' :
4220
			global $current_user;
4221
			$is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
4222
			$master_userdata = get_userdata( Jetpack_Options::get_option( 'master_user' ) );
4223
			if ( $is_master_user ) {
4224
				$this->message = __( 'You have successfully set yourself as Jetpack’s primary user.', 'jetpack' );
4225
			} else {
4226
				$this->message = sprintf( _x( 'You have successfully set %s as Jetpack’s primary user.', '%s is a username', 'jetpack' ), $master_userdata->user_login );
4227
			}
4228
			break;
4229
		}
4230
4231
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
4232
4233
		if ( ! empty( $deactivated_plugins ) ) {
4234
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4235
			$deactivated_titles  = array();
4236
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4237
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
4238
					continue;
4239
				}
4240
4241
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
4242
			}
4243
4244
			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...
4245
				if ( $this->message ) {
4246
					$this->message .= "<br /><br />\n";
4247
				}
4248
4249
				$this->message .= wp_sprintf(
4250
					_n(
4251
						'Jetpack contains the most recent version of the old %l plugin.',
4252
						'Jetpack contains the most recent versions of the old %l plugins.',
4253
						count( $deactivated_titles ),
4254
						'jetpack'
4255
					),
4256
					$deactivated_titles
4257
				);
4258
4259
				$this->message .= "<br />\n";
4260
4261
				$this->message .= _n(
4262
					'The old version has been deactivated and can be removed from your site.',
4263
					'The old versions have been deactivated and can be removed from your site.',
4264
					count( $deactivated_titles ),
4265
					'jetpack'
4266
				);
4267
			}
4268
		}
4269
4270
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
4271
4272
		if ( $this->message || $this->error || $this->privacy_checks || $this->can_display_jetpack_manage_notice() ) {
4273
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4274
		}
4275
4276 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
4277
			/**
4278
			 * Fires when a module configuration page is loaded.
4279
			 * The dynamic part of the hook is the configure parameter from the URL.
4280
			 *
4281
			 * @since 1.1.0
4282
			 */
4283
			do_action( 'jetpack_module_configuration_load_' . $_GET['configure'] );
4284
		}
4285
4286
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4287
	}
4288
4289
	function admin_notices() {
4290
4291
		if ( $this->error ) {
4292
?>
4293
<div id="message" class="jetpack-message jetpack-err">
4294
	<div class="squeezer">
4295
		<h2><?php echo wp_kses( $this->error, array( 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
4296
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
4297
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4298
<?php	endif; ?>
4299
	</div>
4300
</div>
4301
<?php
4302
		}
4303
4304
		if ( $this->message ) {
4305
?>
4306
<div id="message" class="jetpack-message">
4307
	<div class="squeezer">
4308
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
4309
	</div>
4310
</div>
4311
<?php
4312
		}
4313
4314
		if ( $this->privacy_checks ) :
4315
			$module_names = $module_slugs = array();
4316
4317
			$privacy_checks = explode( ',', $this->privacy_checks );
4318
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4319
			foreach ( $privacy_checks as $module_slug ) {
4320
				$module = Jetpack::get_module( $module_slug );
4321
				if ( ! $module ) {
4322
					continue;
4323
				}
4324
4325
				$module_slugs[] = $module_slug;
4326
				$module_names[] = "<strong>{$module['name']}</strong>";
4327
			}
4328
4329
			$module_slugs = join( ',', $module_slugs );
4330
?>
4331
<div id="message" class="jetpack-message jetpack-err">
4332
	<div class="squeezer">
4333
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4334
		<p><?php
4335
			echo wp_kses(
4336
				wptexturize(
4337
					wp_sprintf(
4338
						_nx(
4339
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4340
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4341
							count( $privacy_checks ),
4342
							'%l = list of Jetpack module/feature names',
4343
							'jetpack'
4344
						),
4345
						$module_names
4346
					)
4347
				),
4348
				array( 'strong' => true )
4349
			);
4350
4351
			echo "\n<br />\n";
4352
4353
			echo wp_kses(
4354
				sprintf(
4355
					_nx(
4356
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4357
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4358
						count( $privacy_checks ),
4359
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4360
						'jetpack'
4361
					),
4362
					wp_nonce_url(
4363
						Jetpack::admin_url(
4364
							array(
4365
								'page'   => 'jetpack',
4366
								'action' => 'deactivate',
4367
								'module' => urlencode( $module_slugs ),
4368
							)
4369
						),
4370
						"jetpack_deactivate-$module_slugs"
4371
					),
4372
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4373
				),
4374
				array( 'a' => array( 'href' => true, 'title' => true ) )
4375
			);
4376
		?></p>
4377
	</div>
4378
</div>
4379
<?php endif;
4380
	// only display the notice if the other stuff is not there
4381
	if( $this->can_display_jetpack_manage_notice() && !  $this->error && ! $this->message && ! $this->privacy_checks ) {
4382
		if( isset( $_GET['page'] ) && 'jetpack' != $_GET['page'] )
4383
			$this->opt_in_jetpack_manage_notice();
4384
		}
4385
	}
4386
4387
	/**
4388
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4389
	 */
4390
	function stat( $group, $detail ) {
4391
		if ( ! isset( $this->stats[ $group ] ) )
4392
			$this->stats[ $group ] = array();
4393
		$this->stats[ $group ][] = $detail;
4394
	}
4395
4396
	/**
4397
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4398
	 */
4399
	function do_stats( $method = '' ) {
4400
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4401
			foreach ( $this->stats as $group => $stats ) {
4402
				if ( is_array( $stats ) && count( $stats ) ) {
4403
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4404
					if ( 'server_side' === $method ) {
4405
						self::do_server_side_stat( $args );
4406
					} else {
4407
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4408
					}
4409
				}
4410
				unset( $this->stats[ $group ] );
4411
			}
4412
		}
4413
	}
4414
4415
	/**
4416
	 * Runs stats code for a one-off, server-side.
4417
	 *
4418
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4419
	 *
4420
	 * @return bool If it worked.
4421
	 */
4422
	static function do_server_side_stat( $args ) {
4423
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4424
		if ( is_wp_error( $response ) )
4425
			return false;
4426
4427
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
4428
			return false;
4429
4430
		return true;
4431
	}
4432
4433
	/**
4434
	 * Builds the stats url.
4435
	 *
4436
	 * @param $args array|string The arguments to append to the URL.
4437
	 *
4438
	 * @return string The URL to be pinged.
4439
	 */
4440
	static function build_stats_url( $args ) {
4441
		$defaults = array(
4442
			'v'    => 'wpcom2',
4443
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4444
		);
4445
		$args     = wp_parse_args( $args, $defaults );
4446
		/**
4447
		 * Filter the URL used as the Stats tracking pixel.
4448
		 *
4449
		 * @since 2.3.2
4450
		 *
4451
		 * @param string $url Base URL used as the Stats tracking pixel.
4452
		 */
4453
		$base_url = apply_filters(
4454
			'jetpack_stats_base_url',
4455
			set_url_scheme( 'http://pixel.wp.com/g.gif' )
4456
		);
4457
		$url      = add_query_arg( $args, $base_url );
4458
		return $url;
4459
	}
4460
4461
	function translate_current_user_to_role() {
4462
		foreach ( $this->capability_translations as $role => $cap ) {
4463
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
4464
				return $role;
4465
			}
4466
		}
4467
4468
		return false;
4469
	}
4470
4471
	function translate_role_to_cap( $role ) {
4472
		if ( ! isset( $this->capability_translations[$role] ) ) {
4473
			return false;
4474
		}
4475
4476
		return $this->capability_translations[$role];
4477
	}
4478
4479
	function sign_role( $role ) {
4480
		if ( ! $user_id = (int) get_current_user_id() ) {
4481
			return false;
4482
		}
4483
4484
		$token = Jetpack_Data::get_access_token();
4485
		if ( ! $token || is_wp_error( $token ) ) {
4486
			return false;
4487
		}
4488
4489
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4490
	}
4491
4492
	function build_connect_url( $raw = false, $redirect = false ) {
4493
		if ( ! Jetpack_Options::get_option( 'blog_token' ) || ! Jetpack_Options::get_option( 'id' ) ) {
4494
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4495
			if( is_network_admin() ) {
4496
			    $url = add_query_arg( 'is_multisite', network_admin_url(
4497
			    'admin.php?page=jetpack-settings' ), $url );
4498
			}
4499
		} else {
4500
			$role = $this->translate_current_user_to_role();
4501
			$signed_role = $this->sign_role( $role );
4502
4503
			$user = wp_get_current_user();
4504
4505
			$redirect = $redirect ? esc_url_raw( $redirect ) : '';
4506
4507
			if( isset( $_REQUEST['is_multisite'] ) ) {
4508
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4509
			}
4510
4511
			$args = urlencode_deep(
4512
				array(
4513
					'response_type' => 'code',
4514
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4515
					'redirect_uri'  => add_query_arg(
4516
						array(
4517
							'action'   => 'authorize',
4518
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4519
							'redirect' => $redirect ? urlencode( $redirect ) : false,
4520
						),
4521
						menu_page_url( 'jetpack', false )
4522
					),
4523
					'state'         => $user->ID,
4524
					'scope'         => $signed_role,
4525
					'user_email'    => $user->user_email,
4526
					'user_login'    => $user->user_login,
4527
					'is_active'     => Jetpack::is_active(),
4528
					'jp_version'    => JETPACK__VERSION,
4529
				)
4530
			);
4531
4532
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4533
		}
4534
4535
		return $raw ? $url : esc_url( $url );
4536
	}
4537
4538
	function build_reconnect_url( $raw = false ) {
4539
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4540
		return $raw ? $url : esc_url( $url );
4541
	}
4542
4543
	public static function admin_url( $args = null ) {
4544
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4545
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4546
		return $url;
4547
	}
4548
4549
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4550
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4551
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4552
	}
4553
4554
	function dismiss_jetpack_notice() {
4555
4556
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4557
			return;
4558
		}
4559
4560
		switch( $_GET['jetpack-notice'] ) {
4561
			case 'dismiss':
4562
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4563
4564
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4565
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4566
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4567
				}
4568
				break;
4569 View Code Duplication
			case 'jetpack-manage-opt-out':
4570
4571
				if ( check_admin_referer( 'jetpack_manage_banner_opt_out' ) ) {
4572
					// Don't show the banner again
4573
4574
					Jetpack_Options::update_option( 'dismissed_manage_banner', true );
4575
					// redirect back to the page that had the notice
4576
					if ( wp_get_referer() ) {
4577
						wp_safe_redirect( wp_get_referer() );
4578
					} else {
4579
						// Take me to Jetpack
4580
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4581
					}
4582
				}
4583
				break;
4584 View Code Duplication
			case 'jetpack-protect-multisite-opt-out':
4585
4586
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4587
					// Don't show the banner again
4588
4589
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4590
					// redirect back to the page that had the notice
4591
					if ( wp_get_referer() ) {
4592
						wp_safe_redirect( wp_get_referer() );
4593
					} else {
4594
						// Take me to Jetpack
4595
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4596
					}
4597
				}
4598
				break;
4599
			case 'jetpack-manage-opt-in':
4600
				if ( check_admin_referer( 'jetpack_manage_banner_opt_in' ) ) {
4601
					// This makes sure that we are redirect to jetpack home so that we can see the Success Message.
4602
4603
					$redirection_url = Jetpack::admin_url();
4604
					remove_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4605
4606
					// Don't redirect form the Jetpack Setting Page
4607
					$referer_parsed = parse_url ( wp_get_referer() );
4608
					// check that we do have a wp_get_referer and the query paramater is set orderwise go to the Jetpack Home
4609
					if ( isset( $referer_parsed['query'] ) && false !== strpos( $referer_parsed['query'], 'page=jetpack_modules' ) ) {
4610
						// Take the user to Jetpack home except when on the setting page
4611
						$redirection_url = wp_get_referer();
4612
						add_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4613
					}
4614
					// Also update the JSON API FULL MANAGEMENT Option
4615
					Jetpack::activate_module( 'manage', false, false );
4616
4617
					// Special Message when option in.
4618
					Jetpack::state( 'optin-manage', 'true' );
4619
					// Activate the Module if not activated already
4620
4621
					// Redirect properly
4622
					wp_safe_redirect( $redirection_url );
4623
4624
				}
4625
				break;
4626
		}
4627
	}
4628
4629
	function debugger_page() {
4630
		nocache_headers();
4631
		if ( ! current_user_can( 'manage_options' ) ) {
4632
			die( '-1' );
0 ignored issues
show
Coding Style Compatibility introduced by
The method debugger_page() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
4633
		}
4634
		Jetpack_Debugger::jetpack_debug_display_handler();
4635
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method debugger_page() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
4636
	}
4637
4638
	public static function admin_screen_configure_module( $module_id ) {
4639
4640
		// User that doesn't have 'jetpack_configure_modules' will never end up here since Jetpack Landing Page woun't let them.
4641
		if ( ! in_array( $module_id, Jetpack::get_active_modules() ) && current_user_can( 'manage_options' ) ) {
4642
			if ( has_action( 'display_activate_module_setting_' . $module_id ) ) {
4643
				/**
4644
				 * Fires to diplay a custom module activation screen.
4645
				 *
4646
				 * To add a module actionation screen use Jetpack::module_configuration_activation_screen method.
4647
				 * Example: Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
4648
				 *
4649
				 * @module manage
4650
				 *
4651
				 * @since 3.8.0
4652
				 *
4653
				 * @param int $module_id Module ID.
4654
				 */
4655
				do_action( 'display_activate_module_setting_' . $module_id );
4656
			} else {
4657
				self::display_activate_module_link( $module_id );
4658
			}
4659
4660
			return false;
4661
		} ?>
4662
4663
		<div id="jp-settings-screen" style="position: relative">
4664
			<h3>
4665
			<?php
4666
				$module = Jetpack::get_module( $module_id );
4667
				echo '<a href="' . Jetpack::admin_url( 'page=jetpack_modules' ) . '">' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</a> &rarr; ';
4668
				printf( __( 'Configure %s', 'jetpack' ), $module['name'] );
4669
			?>
4670
			</h3>
4671
			<?php
4672
				/**
4673
				 * Fires within the displayed message when a feature configuation is updated.
4674
				 *
4675
				 * @since 3.4.0
4676
				 *
4677
				 * @param int $module_id Module ID.
4678
				 */
4679
				do_action( 'jetpack_notices_update_settings', $module_id );
4680
				/**
4681
				 * Fires when a feature configuation screen is loaded.
4682
				 * The dynamic part of the hook, $module_id, is the module ID.
4683
				 *
4684
				 * @since 1.1.0
4685
				 */
4686
				do_action( 'jetpack_module_configuration_screen_' . $module_id );
4687
			?>
4688
		</div><?php
4689
	}
4690
4691
	/**
4692
	 * Display link to activate the module to see the settings screen.
4693
	 * @param  string $module_id
4694
	 * @return null
4695
	 */
4696
	public static function display_activate_module_link( $module_id ) {
4697
4698
		$info =  Jetpack::get_module( $module_id );
4699
		$extra = '';
4700
		$activate_url = wp_nonce_url(
4701
				Jetpack::admin_url(
4702
					array(
4703
						'page'   => 'jetpack',
4704
						'action' => 'activate',
4705
						'module' => $module_id,
4706
					)
4707
				),
4708
				"jetpack_activate-$module_id"
4709
			);
4710
4711
		?>
4712
4713
		<div class="wrap configure-module">
4714
			<div id="jp-settings-screen">
4715
				<?php
4716
				if ( $module_id == 'json-api' ) {
4717
4718
					$info['name'] = esc_html__( 'Activate Site Management and JSON API', 'jetpack' );
4719
4720
					$activate_url = Jetpack::init()->opt_in_jetpack_manage_url();
4721
4722
					$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' ), 'http://jetpack.me/support/site-management' );
4723
4724
					// $extra = __( 'To use Site Management, you need to first activate JSON API to allow remote management of your site. ', 'jetpack' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4725
				} ?>
4726
4727
				<h3><?php echo esc_html( $info['name'] ); ?></h3>
4728
				<div class="narrow">
4729
					<p><?php echo  $info['description']; ?></p>
4730
					<?php if( $extra ) { ?>
4731
					<p><?php echo esc_html( $extra ); ?></p>
4732
					<?php } ?>
4733
					<p>
4734
						<?php
4735
						if( wp_get_referer() ) {
4736
							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() );
4737
						} else {
4738
							printf( __( '<a class="button-primary" href="%s">Activate Now</a>', 'jetpack' ) , $activate_url  );
4739
						} ?>
4740
					</p>
4741
				</div>
4742
4743
			</div>
4744
		</div>
4745
4746
		<?php
4747
	}
4748
4749
	public static function sort_modules( $a, $b ) {
4750
		if ( $a['sort'] == $b['sort'] )
4751
			return 0;
4752
4753
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4754
	}
4755
4756 View Code Duplication
	function sync_reindex_trigger() {
4757
		if ( $this->current_user_is_connection_owner() && current_user_can( 'manage_options' ) ) {
4758
			echo json_encode( $this->sync->reindex_trigger() );
4759
		} else {
4760
			echo '{"status":"ERROR"}';
4761
		}
4762
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sync_reindex_trigger() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
4763
	}
4764
4765 View Code Duplication
	function sync_reindex_status(){
4766
		if ( $this->current_user_is_connection_owner() && current_user_can( 'manage_options' ) ) {
4767
			echo json_encode( $this->sync->reindex_status() );
4768
		} else {
4769
			echo '{"status":"ERROR"}';
4770
		}
4771
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sync_reindex_status() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
4772
	}
4773
4774
/* Client API */
4775
4776
	/**
4777
	 * Returns the requested Jetpack API URL
4778
	 *
4779
	 * @return string
4780
	 */
4781
	public static function api_url( $relative_url ) {
4782
		return trailingslashit( JETPACK__API_BASE . $relative_url  ) . JETPACK__API_VERSION . '/';
4783
	}
4784
4785
	/**
4786
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
4787
	 */
4788
	public static function fix_url_for_bad_hosts( $url ) {
4789
		if ( 0 !== strpos( $url, 'https://' ) ) {
4790
			return $url;
4791
		}
4792
4793
		switch ( JETPACK_CLIENT__HTTPS ) {
4794
			case 'ALWAYS' :
4795
				return $url;
4796
			case 'NEVER' :
4797
				return set_url_scheme( $url, 'http' );
4798
			// default : case 'AUTO' :
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4799
		}
4800
4801
		// Yay! Your host is good!
4802
		if ( self::permit_ssl() && wp_http_supports( array( 'ssl' => true ) ) ) {
4803
			return $url;
4804
		}
4805
4806
		// Boo! Your host is bad and makes Jetpack cry!
4807
		return set_url_scheme( $url, 'http' );
4808
	}
4809
4810
	/**
4811
	 * Checks to see if the URL is using SSL to connect with Jetpack
4812
	 *
4813
	 * @since 2.3.3
4814
	 * @return boolean
4815
	 */
4816
	public static function permit_ssl( $force_recheck = false ) {
4817
		// Do some fancy tests to see if ssl is being supported
4818
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
4819
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4820
				$ssl = 0;
4821
			} else {
4822
				switch ( JETPACK_CLIENT__HTTPS ) {
4823
					case 'NEVER':
4824
						$ssl = 0;
4825
						break;
4826
					case 'ALWAYS':
4827
					case 'AUTO':
4828
					default:
4829
						$ssl = 1;
4830
						break;
4831
				}
4832
4833
				// If it's not 'NEVER', test to see
4834
				if ( $ssl ) {
4835
					$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4836
					if ( is_wp_error( $response ) || ( 'OK' !== wp_remote_retrieve_body( $response ) ) ) {
4837
						$ssl = 0;
4838
					}
4839
				}
4840
			}
4841
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4842
		}
4843
4844
		return (bool) $ssl;
4845
	}
4846
4847
	/*
4848
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'ALWAYS' but SSL isn't working.
4849
	 */
4850
	public function alert_required_ssl_fail() {
4851
		if ( ! current_user_can( 'manage_options' ) )
4852
			return;
4853
		?>
4854
4855
		<div id="message" class="error jetpack-message jp-identity-crisis">
4856
			<div class="jp-banner__content">
4857
				<h2><?php _e( 'Something is being cranky!', 'jetpack' ); ?></h2>
4858
				<p><?php _e( 'Your site is configured to only permit SSL connections to Jetpack, but SSL connections don\'t seem to be functional!', 'jetpack' ); ?></p>
4859
			</div>
4860
		</div>
4861
4862
		<?php
4863
	}
4864
4865
	/**
4866
	 * Returns the Jetpack XML-RPC API
4867
	 *
4868
	 * @return string
4869
	 */
4870
	public static function xmlrpc_api_url() {
4871
		$base = preg_replace( '#(https?://[^?/]+)(/?.*)?$#', '\\1', JETPACK__API_BASE );
4872
		return untrailingslashit( $base ) . '/xmlrpc.php';
4873
	}
4874
4875
	/**
4876
	 * Creates two secret tokens and the end of life timestamp for them.
4877
	 *
4878
	 * Note these tokens are unique per call, NOT static per site for connecting.
4879
	 *
4880
	 * @since 2.6
4881
	 * @return array
4882
	 */
4883
	public function generate_secrets() {
4884
	    $secrets = array(
4885
		wp_generate_password( 32, false ), // secret_1
4886
		wp_generate_password( 32, false ), // secret_2
4887
		( time() + 600 ), // eol ( End of Life )
4888
	    );
4889
4890
	    return $secrets;
4891
	}
4892
4893
	/**
4894
	 * Builds the timeout limit for queries talking with the wpcom servers.
4895
	 *
4896
	 * Based on local php max_execution_time in php.ini
4897
	 *
4898
	 * @since 2.6
4899
	 * @return int
4900
	 **/
4901
	public function get_remote_query_timeout_limit() {
4902
	    $timeout = (int) ini_get( 'max_execution_time' );
4903
	    if ( ! $timeout ) // Ensure exec time set in php.ini
4904
		$timeout = 30;
4905
	    return intval( $timeout / 2 );
4906
	}
4907
4908
4909
	/**
4910
	 * Takes the response from the Jetpack register new site endpoint and
4911
	 * verifies it worked properly.
4912
	 *
4913
	 * @since 2.6
4914
	 * @return true or Jetpack_Error
4915
	 **/
4916
	public function validate_remote_register_response( $response ) {
4917
	    	if ( is_wp_error( $response ) ) {
4918
			return new Jetpack_Error( 'register_http_request_failed', $response->get_error_message() );
4919
		}
4920
4921
		$code   = wp_remote_retrieve_response_code( $response );
4922
		$entity = wp_remote_retrieve_body( $response );
4923
		if ( $entity )
4924
			$json = json_decode( $entity );
4925
		else
4926
			$json = false;
4927
4928
		$code_type = intval( $code / 100 );
4929
		if ( 5 == $code_type ) {
4930
			return new Jetpack_Error( 'wpcom_5??', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4931
		} elseif ( 408 == $code ) {
4932
			return new Jetpack_Error( 'wpcom_408', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4933
		} elseif ( ! empty( $json->error ) ) {
4934
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
4935
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
4936
		} elseif ( 200 != $code ) {
4937
			return new Jetpack_Error( 'wpcom_bad_response', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4938
		}
4939
4940
		// Jetpack ID error block
4941
		if ( empty( $json->jetpack_id ) ) {
4942
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack' ), $entity ), $entity );
4943
		} elseif ( ! is_scalar( $json->jetpack_id ) ) {
4944
			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 );
4945
		} elseif ( preg_match( '/[^0-9]/', $json->jetpack_id ) ) {
4946
			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 );
4947
		}
4948
4949
	    return true;
4950
	}
4951
	/**
4952
	 * @return bool|WP_Error
4953
	 */
4954
	public static function register() {
4955
		add_action( 'pre_update_jetpack_option_register', array( 'Jetpack_Options', 'delete_option' ) );
4956
		$secrets = Jetpack::init()->generate_secrets();
4957
4958
		Jetpack_Options::update_option( 'register', $secrets[0] . ':' . $secrets[1] . ':' . $secrets[2] );
4959
4960
		@list( $secret_1, $secret_2, $secret_eol ) = explode( ':', Jetpack_Options::get_option( 'register' ) );
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...
4961
		if ( empty( $secret_1 ) || empty( $secret_2 ) || empty( $secret_eol ) || $secret_eol < time() ) {
4962
			return new Jetpack_Error( 'missing_secrets' );
4963
		}
4964
4965
		$timeout = Jetpack::init()->get_remote_query_timeout_limit();
4966
4967
		$gmt_offset = get_option( 'gmt_offset' );
4968
		if ( ! $gmt_offset ) {
4969
			$gmt_offset = 0;
4970
		}
4971
4972
		$stats_options = get_option( 'stats_options' );
4973
		$stats_id = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
4974
4975
		$args = array(
4976
			'method'  => 'POST',
4977
			'body'    => array(
4978
				'siteurl'         => site_url(),
4979
				'home'            => home_url(),
4980
				'gmt_offset'      => $gmt_offset,
4981
				'timezone_string' => (string) get_option( 'timezone_string' ),
4982
				'site_name'       => (string) get_option( 'blogname' ),
4983
				'secret_1'        => $secret_1,
4984
				'secret_2'        => $secret_2,
4985
				'site_lang'       => get_locale(),
4986
				'timeout'         => $timeout,
4987
				'stats_id'        => $stats_id,
4988
			),
4989
			'headers' => array(
4990
				'Accept' => 'application/json',
4991
			),
4992
			'timeout' => $timeout,
4993
		);
4994
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'register' ) ), $args, true );
4995
4996
4997
		// Make sure the response is valid and does not contain any Jetpack errors
4998
		$valid_response = Jetpack::init()->validate_remote_register_response( $response );
4999
		if( is_wp_error( $valid_response ) || !$valid_response ) {
5000
		    return $valid_response;
5001
		}
5002
5003
5004
		// Grab the response values to work with
5005
		$code   = wp_remote_retrieve_response_code( $response );
5006
		$entity = wp_remote_retrieve_body( $response );
5007
5008
		if ( $entity )
5009
			$json = json_decode( $entity );
5010
		else
5011
			$json = false;
5012
5013 View Code Duplication
		if ( empty( $json->jetpack_secret ) || ! is_string( $json->jetpack_secret ) )
5014
			return new Jetpack_Error( 'jetpack_secret', '', $code );
5015
5016
		if ( isset( $json->jetpack_public ) ) {
5017
			$jetpack_public = (int) $json->jetpack_public;
5018
		} else {
5019
			$jetpack_public = false;
5020
		}
5021
5022
		Jetpack_Options::update_options(
5023
			array(
5024
				'id'         => (int)    $json->jetpack_id,
5025
				'blog_token' => (string) $json->jetpack_secret,
5026
				'public'     => $jetpack_public,
5027
			)
5028
		);
5029
5030
		/**
5031
		 * Fires when a site is registered on WordPress.com.
5032
		 *
5033
		 * @since 3.7.0
5034
		 *
5035
		 * @param int $json->jetpack_id Jetpack Blog ID.
5036
		 * @param string $json->jetpack_secret Jetpack Blog Token.
5037
		 * @param int|bool $jetpack_public Is the site public.
5038
		 */
5039
		do_action( 'jetpack_site_registered', $json->jetpack_id, $json->jetpack_secret, $jetpack_public );
5040
5041
		// Initialize Jump Start for the first and only time.
5042
		if ( ! Jetpack_Options::get_option( 'jumpstart' ) ) {
5043
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
5044
5045
			$jetpack = Jetpack::init();
5046
5047
			$jetpack->stat( 'jumpstart', 'unique-views' );
5048
			$jetpack->do_stats( 'server_side' );
5049
		};
5050
5051
		return true;
5052
	}
5053
5054
	/**
5055
	 * If the db version is showing something other that what we've got now, bump it to current.
5056
	 *
5057
	 * @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...
5058
	 */
5059
	public static function maybe_set_version_option() {
5060
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5061
		if ( JETPACK__VERSION != $version ) {
5062
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5063
			return true;
5064
		}
5065
		return false;
5066
	}
5067
5068
/* Client Server API */
5069
5070
	/**
5071
	 * Loads the Jetpack XML-RPC client
5072
	 */
5073
	public static function load_xml_rpc_client() {
5074
		require_once ABSPATH . WPINC . '/class-IXR.php';
5075
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-ixr-client.php';
5076
	}
5077
5078
	function verify_xml_rpc_signature() {
5079
		if ( $this->xmlrpc_verification ) {
5080
			return $this->xmlrpc_verification;
5081
		}
5082
5083
		// It's not for us
5084
		if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
5085
			return false;
5086
		}
5087
5088
		@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...
5089
		if (
5090
			empty( $token_key )
5091
		||
5092
			empty( $version ) || strval( JETPACK__API_VERSION ) !== $version
5093
		) {
5094
			return false;
5095
		}
5096
5097
		if ( '0' === $user_id ) {
5098
			$token_type = 'blog';
5099
			$user_id = 0;
5100
		} else {
5101
			$token_type = 'user';
5102
			if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
5103
				return false;
5104
			}
5105
			$user_id = (int) $user_id;
5106
5107
			$user = new WP_User( $user_id );
5108
			if ( ! $user || ! $user->exists() ) {
5109
				return false;
5110
			}
5111
		}
5112
5113
		$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...
5114
		if ( ! $token ) {
5115
			return false;
5116
		}
5117
5118
		$token_check = "$token_key.";
5119
		if ( ! hash_equals( substr( $token->secret, 0, strlen( $token_check ) ), $token_check ) ) {
5120
			return false;
5121
		}
5122
5123
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
5124
5125
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5126
		if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
5127
			$post_data   = $_POST;
5128
			$file_hashes = array();
5129
			foreach ( $post_data as $post_data_key => $post_data_value ) {
5130
				if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
5131
					continue;
5132
				}
5133
				$post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
5134
				$file_hashes[$post_data_key] = $post_data_value;
5135
			}
5136
5137
			foreach ( $file_hashes as $post_data_key => $post_data_value ) {
5138
				unset( $post_data["_jetpack_file_hmac_{$post_data_key}"] );
5139
				$post_data[$post_data_key] = $post_data_value;
5140
			}
5141
5142
			ksort( $post_data );
5143
5144
			$body = http_build_query( stripslashes_deep( $post_data ) );
5145
		} elseif ( is_null( $this->HTTP_RAW_POST_DATA ) ) {
5146
			$body = file_get_contents( 'php://input' );
5147
		} else {
5148
			$body = null;
5149
		}
5150
		$signature = $jetpack_signature->sign_current_request(
5151
			array( 'body' => is_null( $body ) ? $this->HTTP_RAW_POST_DATA : $body, )
5152
		);
5153
5154
		if ( ! $signature ) {
5155
			return false;
5156
		} else if ( is_wp_error( $signature ) ) {
5157
			return $signature;
5158
		} else if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
5159
			return false;
5160
		}
5161
5162
		$timestamp = (int) $_GET['timestamp'];
5163
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5164
5165
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5166
			return false;
5167
		}
5168
5169
		$this->xmlrpc_verification = array(
5170
			'type'    => $token_type,
5171
			'user_id' => $token->external_user_id,
5172
		);
5173
5174
		return $this->xmlrpc_verification;
5175
	}
5176
5177
	/**
5178
	 * Authenticates XML-RPC and other requests from the Jetpack Server
5179
	 */
5180
	function authenticate_jetpack( $user, $username, $password ) {
0 ignored issues
show
Unused Code introduced by
The parameter $username is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $password is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
5181
		if ( is_a( $user, 'WP_User' ) ) {
5182
			return $user;
5183
		}
5184
5185
		$token_details = $this->verify_xml_rpc_signature();
5186
5187
		if ( ! $token_details || is_wp_error( $token_details ) ) {
5188
			return $user;
5189
		}
5190
5191
		if ( 'user' !== $token_details['type'] ) {
5192
			return $user;
5193
		}
5194
5195
		if ( ! $token_details['user_id'] ) {
5196
			return $user;
5197
		}
5198
5199
		nocache_headers();
5200
5201
		return new WP_User( $token_details['user_id'] );
5202
	}
5203
5204
	function add_nonce( $timestamp, $nonce ) {
5205
		global $wpdb;
5206
		static $nonces_used_this_request = array();
5207
5208
		if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) {
5209
			return $nonces_used_this_request["$timestamp:$nonce"];
5210
		}
5211
5212
		// This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce
5213
		$timestamp = (int) $timestamp;
5214
		$nonce     = esc_sql( $nonce );
5215
5216
		// Raw query so we can avoid races: add_option will also update
5217
		$show_errors = $wpdb->show_errors( false );
5218
5219
		$old_nonce = $wpdb->get_row(
5220
			$wpdb->prepare( "SELECT * FROM `$wpdb->options` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
5221
		);
5222
5223
		if ( is_null( $old_nonce ) ) {
5224
			$return = $wpdb->query(
5225
				$wpdb->prepare(
5226
					"INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
5227
					"jetpack_nonce_{$timestamp}_{$nonce}",
5228
					time(),
5229
					'no'
5230
				)
5231
			);
5232
		} else {
5233
			$return = false;
5234
		}
5235
5236
		$wpdb->show_errors( $show_errors );
5237
5238
		$nonces_used_this_request["$timestamp:$nonce"] = $return;
5239
5240
		return $return;
5241
	}
5242
5243
	/**
5244
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5245
	 * Capture it here so we can verify the signature later.
5246
	 */
5247
	function xmlrpc_methods( $methods ) {
5248
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5249
		return $methods;
5250
	}
5251
5252
	function public_xmlrpc_methods( $methods ) {
5253
		if ( array_key_exists( 'wp.getOptions', $methods ) ) {
5254
			$methods['wp.getOptions'] = array( $this, 'jetpack_getOptions' );
5255
		}
5256
		return $methods;
5257
	}
5258
5259
	function jetpack_getOptions( $args ) {
5260
		global $wp_xmlrpc_server;
5261
5262
		$wp_xmlrpc_server->escape( $args );
5263
5264
		$username	= $args[1];
5265
		$password	= $args[2];
5266
5267
		if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
5268
			return $wp_xmlrpc_server->error;
5269
		}
5270
5271
		$options = array();
5272
		$user_data = $this->get_connected_user_data();
5273
		if ( is_array( $user_data ) ) {
5274
			$options['jetpack_user_id'] = array(
5275
				'desc'          => __( 'The WP.com user ID of the connected user', 'jetpack' ),
5276
				'readonly'      => true,
5277
				'value'         => $user_data['ID'],
5278
			);
5279
			$options['jetpack_user_login'] = array(
5280
				'desc'          => __( 'The WP.com username of the connected user', 'jetpack' ),
5281
				'readonly'      => true,
5282
				'value'         => $user_data['login'],
5283
			);
5284
			$options['jetpack_user_email'] = array(
5285
				'desc'          => __( 'The WP.com user email of the connected user', 'jetpack' ),
5286
				'readonly'      => true,
5287
				'value'         => $user_data['email'],
5288
			);
5289
			$options['jetpack_user_site_count'] = array(
5290
				'desc'          => __( 'The number of sites of the connected WP.com user', 'jetpack' ),
5291
				'readonly'      => true,
5292
				'value'         => $user_data['site_count'],
5293
			);
5294
		}
5295
		$wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
5296
		$args = stripslashes_deep( $args );
5297
		return $wp_xmlrpc_server->wp_getOptions( $args );
5298
	}
5299
5300
	function xmlrpc_options( $options ) {
5301
		$jetpack_client_id = false;
5302
		if ( self::is_active() ) {
5303
			$jetpack_client_id = Jetpack_Options::get_option( 'id' );
5304
		}
5305
		$options['jetpack_version'] = array(
5306
				'desc'          => __( 'Jetpack Plugin Version', 'jetpack' ),
5307
				'readonly'      => true,
5308
				'value'         => JETPACK__VERSION,
5309
		);
5310
5311
		$options['jetpack_client_id'] = array(
5312
				'desc'          => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack' ),
5313
				'readonly'      => true,
5314
				'value'         => $jetpack_client_id,
5315
		);
5316
		return $options;
5317
	}
5318
5319
	public static function clean_nonces( $all = false ) {
5320
		global $wpdb;
5321
5322
		$sql = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE %s";
5323
		if ( method_exists ( $wpdb , 'esc_like' ) ) {
5324
			$sql_args = array( $wpdb->esc_like( 'jetpack_nonce_' ) . '%' );
5325
		} else {
5326
			$sql_args = array( like_escape( 'jetpack_nonce_' ) . '%' );
5327
		}
5328
5329
		if ( true !== $all ) {
5330
			$sql .= ' AND CAST( `option_value` AS UNSIGNED ) < %d';
5331
			$sql_args[] = time() - 3600;
5332
		}
5333
5334
		$sql .= ' ORDER BY `option_id` LIMIT 100';
5335
5336
		$sql = $wpdb->prepare( $sql, $sql_args );
5337
5338
		for ( $i = 0; $i < 1000; $i++ ) {
5339
			if ( ! $wpdb->query( $sql ) ) {
5340
				break;
5341
			}
5342
		}
5343
	}
5344
5345
	/**
5346
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5347
	 * SET: state( $key, $value );
5348
	 * GET: $value = state( $key );
5349
	 *
5350
	 * @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...
5351
	 * @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...
5352
	 * @param bool $restate private
5353
	 */
5354
	public static function state( $key = null, $value = null, $restate = false ) {
5355
		static $state = array();
5356
		static $path, $domain;
5357
		if ( ! isset( $path ) ) {
5358
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
5359
			$admin_url = Jetpack::admin_url();
5360
			$bits      = parse_url( $admin_url );
5361
5362
			if ( is_array( $bits ) ) {
5363
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5364
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5365
			} else {
5366
				$path = $domain = null;
5367
			}
5368
		}
5369
5370
		// Extract state from cookies and delete cookies
5371
		if ( isset( $_COOKIE[ 'jetpackState' ] ) && is_array( $_COOKIE[ 'jetpackState' ] ) ) {
5372
			$yum = $_COOKIE[ 'jetpackState' ];
5373
			unset( $_COOKIE[ 'jetpackState' ] );
5374
			foreach ( $yum as $k => $v ) {
5375
				if ( strlen( $v ) )
5376
					$state[ $k ] = $v;
5377
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5378
			}
5379
		}
5380
5381
		if ( $restate ) {
5382
			foreach ( $state as $k => $v ) {
5383
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5384
			}
5385
			return;
5386
		}
5387
5388
		// Get a state variable
5389
		if ( isset( $key ) && ! isset( $value ) ) {
5390
			if ( array_key_exists( $key, $state ) )
5391
				return $state[ $key ];
5392
			return null;
5393
		}
5394
5395
		// Set a state variable
5396
		if ( isset ( $key ) && isset( $value ) ) {
5397
			if( is_array( $value ) && isset( $value[0] ) ) {
5398
				$value = $value[0];
5399
			}
5400
			$state[ $key ] = $value;
5401
			setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5402
		}
5403
	}
5404
5405
	public static function restate() {
5406
		Jetpack::state( null, null, true );
5407
	}
5408
5409
	public static function check_privacy( $file ) {
5410
		static $is_site_publicly_accessible = null;
5411
5412
		if ( is_null( $is_site_publicly_accessible ) ) {
5413
			$is_site_publicly_accessible = false;
5414
5415
			Jetpack::load_xml_rpc_client();
5416
			$rpc = new Jetpack_IXR_Client();
5417
5418
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5419
			if ( $success ) {
5420
				$response = $rpc->getResponse();
5421
				if ( $response ) {
5422
					$is_site_publicly_accessible = true;
5423
				}
5424
			}
5425
5426
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5427
		}
5428
5429
		if ( $is_site_publicly_accessible ) {
5430
			return;
5431
		}
5432
5433
		$module_slug = self::get_module_slug( $file );
5434
5435
		$privacy_checks = Jetpack::state( 'privacy_checks' );
5436
		if ( ! $privacy_checks ) {
5437
			$privacy_checks = $module_slug;
5438
		} else {
5439
			$privacy_checks .= ",$module_slug";
5440
		}
5441
5442
		Jetpack::state( 'privacy_checks', $privacy_checks );
5443
	}
5444
5445
	/**
5446
	 * Helper method for multicall XMLRPC.
5447
	 */
5448
	public static function xmlrpc_async_call() {
5449
		global $blog_id;
5450
		static $clients = array();
5451
5452
		$client_blog_id = is_multisite() ? $blog_id : 0;
5453
5454
		if ( ! isset( $clients[$client_blog_id] ) ) {
5455
			Jetpack::load_xml_rpc_client();
5456
			$clients[$client_blog_id] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER, ) );
5457
			if ( function_exists( 'ignore_user_abort' ) ) {
5458
				ignore_user_abort( true );
5459
			}
5460
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5461
		}
5462
5463
		$args = func_get_args();
5464
5465
		if ( ! empty( $args[0] ) ) {
5466
			call_user_func_array( array( $clients[$client_blog_id], 'addCall' ), $args );
5467
		} elseif ( is_multisite() ) {
5468
			foreach ( $clients as $client_blog_id => $client ) {
5469
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5470
					continue;
5471
				}
5472
5473
				$switch_success = switch_to_blog( $client_blog_id, true );
5474
				if ( ! $switch_success ) {
5475
					continue;
5476
				}
5477
5478
				flush();
5479
				$client->query();
5480
5481
				restore_current_blog();
5482
			}
5483
		} else {
5484
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5485
				flush();
5486
				$clients[0]->query();
5487
			}
5488
		}
5489
	}
5490
5491
	public static function staticize_subdomain( $url ) {
5492
5493
		// Extract hostname from URL
5494
		$host = parse_url( $url, PHP_URL_HOST );
5495
5496
		// Explode hostname on '.'
5497
		$exploded_host = explode( '.', $host );
5498
5499
		// Retrieve the name and TLD
5500
		if ( count( $exploded_host ) > 1 ) {
5501
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
5502
			$tld = $exploded_host[ count( $exploded_host ) - 1 ];
5503
			// Rebuild domain excluding subdomains
5504
			$domain = $name . '.' . $tld;
5505
		} else {
5506
			$domain = $host;
5507
		}
5508
		// Array of Automattic domains
5509
		$domain_whitelist = array( 'wordpress.com', 'wp.com' );
5510
5511
		// Return $url if not an Automattic domain
5512
		if ( ! in_array( $domain, $domain_whitelist ) ) {
5513
			return $url;
5514
		}
5515
5516
		if ( is_ssl() ) {
5517
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
5518
		}
5519
5520
		srand( crc32( basename( $url ) ) );
5521
		$static_counter = rand( 0, 2 );
5522
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
5523
5524
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
5525
	}
5526
5527
/* JSON API Authorization */
5528
5529
	/**
5530
	 * Handles the login action for Authorizing the JSON API
5531
	 */
5532
	function login_form_json_api_authorization() {
5533
		$this->verify_json_api_authorization_request();
5534
5535
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
5536
5537
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
5538
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5539
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
5540
	}
5541
5542
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
5543
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
5544
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
5545
			return $url;
5546
		}
5547
5548
		$parsed_url = parse_url( $url );
5549
		$url = strtok( $url, '?' );
5550
		$url = "$url?{$_SERVER['QUERY_STRING']}";
5551
		if ( ! empty( $parsed_url['query'] ) )
5552
			$url .= "&{$parsed_url['query']}";
5553
5554
		return $url;
5555
	}
5556
5557
	// Make sure the POSTed request is handled by the same action
5558
	function preserve_action_in_login_form_for_json_api_authorization() {
5559
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
5560
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
5561
	}
5562
5563
	// If someone logs in to approve API access, store the Access Code in usermeta
5564
	function store_json_api_authorization_token( $user_login, $user ) {
5565
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5566
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
5567
		$token = wp_generate_password( 32, false );
5568
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5569
	}
5570
5571
	// Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access
5572
	function allow_wpcom_public_api_domain( $domains ) {
5573
		$domains[] = 'public-api.wordpress.com';
5574
		return $domains;
5575
	}
5576
5577
	// Add the Access Code details to the public-api.wordpress.com redirect
5578
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5579
		return add_query_arg(
5580
			urlencode_deep(
5581
				array(
5582
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5583
					'jetpack-user-id' => (int) $user->ID,
5584
					'jetpack-state'   => $this->json_api_authorization_request['state'],
5585
				)
5586
			),
5587
			$redirect_to
5588
		);
5589
	}
5590
5591
	// Verifies the request by checking the signature
5592
	function verify_json_api_authorization_request() {
5593
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
5594
5595
		$token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
5596
		if ( ! $token || empty( $token->secret ) ) {
5597
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' , 'jetpack' ) );
5598
		}
5599
5600
		$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' );
5601
5602
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5603
5604
		if ( isset( $_POST['jetpack_json_api_original_query'] ) ) {
5605
			$signature = $jetpack_signature->sign_request( $_GET['token'], $_GET['timestamp'], $_GET['nonce'], '', 'GET', $_POST['jetpack_json_api_original_query'], null, true );
5606
		} else {
5607
			$signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) );
5608
		}
5609
5610
		if ( ! $signature ) {
5611
			wp_die( $die_error );
5612
		} else if ( is_wp_error( $signature ) ) {
5613
			wp_die( $die_error );
5614
		} else if ( $signature !== $_GET['signature'] ) {
5615
			if ( is_ssl() ) {
5616
				// 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
5617
				$signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) );
5618
				if ( ! $signature || is_wp_error( $signature ) || $signature !== $_GET['signature'] ) {
5619
					wp_die( $die_error );
5620
				}
5621
			} else {
5622
				wp_die( $die_error );
5623
			}
5624
		}
5625
5626
		$timestamp = (int) $_GET['timestamp'];
5627
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5628
5629
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5630
			// De-nonce the nonce, at least for 5 minutes.
5631
			// 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)
5632
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5633
			if ( $old_nonce_time < time() - 300 ) {
5634
				wp_die( __( 'The authorization process expired.  Please go back and try again.' , 'jetpack' ) );
5635
			}
5636
		}
5637
5638
		$data = json_decode( base64_decode( stripslashes( $_GET['data'] ) ) );
5639
		$data_filters = array(
5640
			'state'        => 'opaque',
5641
			'client_id'    => 'int',
5642
			'client_title' => 'string',
5643
			'client_image' => 'url',
5644
		);
5645
5646
		foreach ( $data_filters as $key => $sanitation ) {
5647
			if ( ! isset( $data->$key ) ) {
5648
				wp_die( $die_error );
5649
			}
5650
5651
			switch ( $sanitation ) {
5652
			case 'int' :
5653
				$this->json_api_authorization_request[$key] = (int) $data->$key;
5654
				break;
5655
			case 'opaque' :
5656
				$this->json_api_authorization_request[$key] = (string) $data->$key;
5657
				break;
5658
			case 'string' :
5659
				$this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() );
5660
				break;
5661
			case 'url' :
5662
				$this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key );
5663
				break;
5664
			}
5665
		}
5666
5667
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5668
			wp_die( $die_error );
5669
		}
5670
	}
5671
5672
	function login_message_json_api_authorization( $message ) {
0 ignored issues
show
Unused Code introduced by
The parameter $message is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
5673
		return '<p class="message">' . sprintf(
5674
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' , 'jetpack' ),
5675
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5676
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5677
	}
5678
5679
	/**
5680
	 * Get $content_width, but with a <s>twist</s> filter.
5681
	 */
5682
	public static function get_content_width() {
5683
		$content_width = isset( $GLOBALS['content_width'] ) ? $GLOBALS['content_width'] : false;
5684
		/**
5685
		 * Filter the Content Width value.
5686
		 *
5687
		 * @since 2.2.3
5688
		 *
5689
		 * @param string $content_width Content Width value.
5690
		 */
5691
		return apply_filters( 'jetpack_content_width', $content_width );
5692
	}
5693
5694
	/**
5695
	 * Centralize the function here until it gets added to core.
5696
	 *
5697
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
5698
	 * @param int $size Size of the avatar image
5699
	 * @param string $default URL to a default image to use if no avatar is available
5700
	 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
5701
	 *
5702
	 * @return array First element is the URL, second is the class.
5703
	 */
5704
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
5705
		// Don't bother adding the __return_true filter if it's already there.
5706
		$has_filter = has_filter( 'pre_option_show_avatars', '__return_true' );
5707
5708
		if ( $force_display && ! $has_filter )
5709
			add_filter( 'pre_option_show_avatars', '__return_true' );
5710
5711
		$avatar = get_avatar( $id_or_email, $size, $default );
5712
5713
		if ( $force_display && ! $has_filter )
5714
			remove_filter( 'pre_option_show_avatars', '__return_true' );
5715
5716
		// If no data, fail out.
5717
		if ( is_wp_error( $avatar ) || ! $avatar )
5718
			return array( null, null );
5719
5720
		// Pull out the URL.  If it's not there, fail out.
5721
		if ( ! preg_match( '/src=["\']([^"\']+)["\']/', $avatar, $url_matches ) )
5722
			return array( null, null );
5723
		$url = wp_specialchars_decode( $url_matches[1], ENT_QUOTES );
5724
5725
		// Pull out the class, but it's not a big deal if it's missing.
5726
		$class = '';
5727
		if ( preg_match( '/class=["\']([^"\']+)["\']/', $avatar, $class_matches ) )
5728
			$class = wp_specialchars_decode( $class_matches[1], ENT_QUOTES );
5729
5730
		return array( $url, $class );
5731
	}
5732
5733
	/**
5734
	 * Pings the WordPress.com Mirror Site for the specified options.
5735
	 *
5736
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
5737
	 *
5738
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
5739
	 */
5740
	public function get_cloud_site_options( $option_names ) {
5741
		$option_names = array_filter( (array) $option_names, 'is_string' );
5742
5743
		Jetpack::load_xml_rpc_client();
5744
		$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
5745
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
5746
		if ( $xml->isError() ) {
5747
			return array(
5748
				'error_code' => $xml->getErrorCode(),
5749
				'error_msg'  => $xml->getErrorMessage(),
5750
			);
5751
		}
5752
		$cloud_site_options = $xml->getResponse();
5753
5754
		return $cloud_site_options;
5755
	}
5756
5757
	/**
5758
	 * Fetch the filtered array of options that we should compare to determine an identity crisis.
5759
	 *
5760
	 * @return array An array of options to check.
5761
	 */
5762
	public static function identity_crisis_options_to_check() {
5763
		$options = array(
5764
			'siteurl',
5765
			'home',
5766
		);
5767
		/**
5768
		 * Filter the options that we should compare to determine an identity crisis.
5769
		 *
5770
		 * @since 2.5.0
5771
		 *
5772
		 * @param array $options Array of options to compare to determine an identity crisis.
5773
		 */
5774
		return apply_filters( 'jetpack_identity_crisis_options_to_check', $options );
5775
	}
5776
5777
	/**
5778
	 * Checks to make sure that local options have the same values as remote options.  Will cache the results for up to 24 hours.
5779
	 *
5780
	 * @param bool $force_recheck Whether to ignore any cached transient and manually re-check.
5781
	 *
5782
	 * @return array An array of options that do not match.  If everything is good, it will evaluate to false.
5783
	 */
5784
	public static function check_identity_crisis( $force_recheck = false ) {
5785
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() )
5786
			return false;
5787
5788
		if ( $force_recheck || false === ( $errors = get_transient( 'jetpack_has_identity_crisis' ) ) ) {
5789
			$options_to_check = self::identity_crisis_options_to_check();
5790
			$cloud_options = Jetpack::init()->get_cloud_site_options( $options_to_check );
5791
			$errors        = array();
5792
5793
			foreach ( $cloud_options as $cloud_key => $cloud_value ) {
5794
5795
				// If it's not the same as the local value...
5796
				if ( $cloud_value !== get_option( $cloud_key ) ) {
5797
5798
					// Break out if we're getting errors.  We are going to check the error keys later when we alert.
5799
					if ( 'error_code' == $cloud_key ) {
5800
						$errors[ $cloud_key ] = $cloud_value;
5801
						break;
5802
					}
5803
5804
					$parsed_cloud_value = parse_url( $cloud_value );
5805
					// If the current options is an IP address
5806
					if ( filter_var( $parsed_cloud_value['host'], FILTER_VALIDATE_IP ) ) {
5807
						// Give the new value a Jetpack to fly in to the clouds
5808
						Jetpack::resolve_identity_crisis( $cloud_key );
5809
						continue;
5810
					}
5811
5812
					// And it's not been added to the whitelist...
5813
					if ( ! self::is_identity_crisis_value_whitelisted( $cloud_key, $cloud_value ) ) {
5814
						/*
5815
						 * This should be a temporary hack until a cleaner solution is found.
5816
						 *
5817
						 * The siteurl and home can be set to use http in General > Settings
5818
						 * however some constants can be defined that can force https in wp-admin
5819
						 * when this happens wpcom can confuse wporg with a fake identity
5820
						 * crisis with a mismatch of http vs https when it should be allowed.
5821
						 * we need to check that here.
5822
						 *
5823
						 * @see https://github.com/Automattic/jetpack/issues/1006
5824
						 */
5825
						if ( ( 'home' == $cloud_key || 'siteurl' == $cloud_key )
5826
							&& ( substr( $cloud_value, 0, 8 ) == "https://" )
5827
							&& Jetpack::init()->is_ssl_required_to_visit_site() ) {
5828
							// Ok, we found a mismatch of http and https because of wp-config, not an invalid url
5829
							continue;
5830
						}
5831
5832
5833
						// Then kick an error!
5834
						$errors[ $cloud_key ] = $cloud_value;
5835
					}
5836
				}
5837
			}
5838
		}
5839
5840
		/**
5841
		 * Filters the errors returned when checking for an Identity Crisis.
5842
		 *
5843
		 * @since 2.3.2
5844
		 *
5845
		 * @param array $errors Array of Identity Crisis errors.
5846
		 * @param bool $force_recheck Ignore any cached transient and manually re-check. Default to false.
5847
		 */
5848
		return apply_filters( 'jetpack_has_identity_crisis', $errors, $force_recheck );
5849
	}
5850
5851
	/*
5852
	 * Resolve ID crisis
5853
	 *
5854
	 * If the URL has changed, but the rest of the options are the same (i.e. blog/user tokens)
5855
	 * The user has the option to update the shadow site with the new URL before a new
5856
	 * token is created.
5857
	 *
5858
	 * @param $key : Which option to sync.  null defautlts to home and siteurl
5859
	 */
5860
	public static function resolve_identity_crisis( $key = null ) {
5861
		if ( $key ) {
5862
			$identity_options = array( $key );
5863
		} else {
5864
			$identity_options = self::identity_crisis_options_to_check();
5865
		}
5866
5867
		if ( is_array( $identity_options ) ) {
5868
			foreach( $identity_options as $identity_option ) {
5869
				Jetpack_Sync::sync_options( __FILE__, $identity_option );
5870
5871
				/**
5872
				 * Fires when a shadow site option is updated.
5873
				 * These options are updated via the Identity Crisis UI.
5874
				 * $identity_option is the option that gets updated.
5875
				 *
5876
				 * @since 3.7.0
5877
				 */
5878
				do_action( "update_option_{$identity_option}" );
5879
			}
5880
		}
5881
	}
5882
5883
	/*
5884
	 * Whitelist URL
5885
	 *
5886
	 * Ignore the URL differences between the blog and the shadow site.
5887
	 */
5888
	public static function whitelist_current_url() {
5889
		$options_to_check = Jetpack::identity_crisis_options_to_check();
5890
		$cloud_options = Jetpack::init()->get_cloud_site_options( $options_to_check );
5891
5892
		foreach ( $cloud_options as $cloud_key => $cloud_value ) {
5893
			Jetpack::whitelist_identity_crisis_value( $cloud_key, $cloud_value );
5894
		}
5895
	}
5896
5897
	/*
5898
	 * Ajax callbacks for ID crisis resolutions
5899
	 *
5900
	 * Things that could happen here:
5901
	 *  - site_migrated : Update the URL on the shadow blog to match new domain
5902
	 *  - whitelist     : Ignore the URL difference
5903
	 *  - default       : Error message
5904
	 */
5905
	public static function resolve_identity_crisis_ajax_callback() {
5906
		check_ajax_referer( 'resolve-identity-crisis', 'ajax-nonce' );
5907
5908
		switch ( $_POST[ 'crisis_resolution_action' ] ) {
5909
			case 'site_migrated':
5910
				Jetpack::resolve_identity_crisis();
5911
				echo 'resolved';
5912
				break;
5913
5914
			case 'whitelist':
5915
				Jetpack::whitelist_current_url();
5916
				echo 'whitelisted';
5917
				break;
5918
5919
			case 'reset_connection':
5920
				// Delete the options first so it doesn't get confused which site to disconnect dotcom-side
5921
				Jetpack_Options::delete_option(
5922
					array(
5923
						'register',
5924
						'blog_token',
5925
						'user_token',
5926
						'user_tokens',
5927
						'master_user',
5928
						'time_diff',
5929
						'fallback_no_verify_ssl_certs',
5930
						'id',
5931
					)
5932
				);
5933
				delete_transient( 'jetpack_has_identity_crisis' );
5934
5935
				echo 'reset-connection-success';
5936
				break;
5937
5938
			default:
5939
				echo 'missing action';
5940
				break;
5941
		}
5942
5943
		wp_die();
5944
	}
5945
5946
	/**
5947
	 * Adds a value to the whitelist for the specified key.
5948
	 *
5949
	 * @param string $key The option name that we're whitelisting the value for.
5950
	 * @param string $value The value that we're intending to add to the whitelist.
5951
	 *
5952
	 * @return bool Whether the value was added to the whitelist, or false if it was already there.
5953
	 */
5954
	public static function whitelist_identity_crisis_value( $key, $value ) {
5955
		if ( Jetpack::is_identity_crisis_value_whitelisted( $key, $value ) ) {
5956
			return false;
5957
		}
5958
5959
		$whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist', array() );
5960
		if ( empty( $whitelist[ $key ] ) || ! is_array( $whitelist[ $key ] ) ) {
5961
			$whitelist[ $key ] = array();
5962
		}
5963
		array_push( $whitelist[ $key ], $value );
5964
5965
		Jetpack_Options::update_option( 'identity_crisis_whitelist', $whitelist );
5966
		return true;
5967
	}
5968
5969
	/**
5970
	 * Checks whether a value is already whitelisted.
5971
	 *
5972
	 * @param string $key The option name that we're checking the value for.
5973
	 * @param string $value The value that we're curious to see if it's on the whitelist.
5974
	 *
5975
	 * @return bool Whether the value is whitelisted.
5976
	 */
5977
	public static function is_identity_crisis_value_whitelisted( $key, $value ) {
5978
		$whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist', array() );
5979
		if ( ! empty( $whitelist[ $key ] ) && is_array( $whitelist[ $key ] ) && in_array( $value, $whitelist[ $key ] ) ) {
5980
			return true;
5981
		}
5982
		return false;
5983
	}
5984
5985
	/**
5986
	 * Checks whether the home and siteurl specifically are whitelisted
5987
	 * Written so that we don't have re-check $key and $value params every time
5988
	 * we want to check if this site is whitelisted, for example in footer.php
5989
	 *
5990
	 * @return bool True = already whitelsisted False = not whitelisted
5991
	 */
5992
	public static function jetpack_is_staging_site() {
5993
		$current_whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist' );
5994
		if ( ! $current_whitelist ) {
5995
			return false;
5996
		}
5997
5998
		$options_to_check  = Jetpack::identity_crisis_options_to_check();
5999
		$cloud_options     = Jetpack::init()->get_cloud_site_options( $options_to_check );
6000
6001
		foreach ( $cloud_options as $cloud_key => $cloud_value ) {
6002
			if ( ! self::is_identity_crisis_value_whitelisted( $cloud_key, $cloud_value ) ) {
6003
				return false;
6004
			}
6005
		}
6006
		return true;
6007
	}
6008
6009
	public function identity_crisis_js( $nonce ) {
6010
?>
6011
<script>
6012
(function( $ ) {
6013
	var SECOND_IN_MS = 1000;
6014
6015
	function contactSupport( e ) {
6016
		e.preventDefault();
6017
		$( '.jp-id-crisis-question' ).hide();
6018
		$( '#jp-id-crisis-contact-support' ).show();
6019
	}
6020
6021
	function autodismissSuccessBanner() {
6022
		$( '.jp-identity-crisis' ).fadeOut(600); //.addClass( 'dismiss' );
6023
	}
6024
6025
	var data = { action: 'jetpack_resolve_identity_crisis', 'ajax-nonce': '<?php echo $nonce; ?>' };
6026
6027
	$( document ).ready(function() {
6028
6029
		// Site moved: Update the URL on the shadow blog
6030
		$( '.site-moved' ).click(function( e ) {
6031
			e.preventDefault();
6032
			data.crisis_resolution_action = 'site_migrated';
6033
			$( '#jp-id-crisis-question-1 .spinner' ).show();
6034
			$.post( ajaxurl, data, function() {
6035
				$( '.jp-id-crisis-question' ).hide();
6036
				$( '.banner-title' ).hide();
6037
				$( '#jp-id-crisis-success' ).show();
6038
				setTimeout( autodismissSuccessBanner, 6 * SECOND_IN_MS );
6039
			});
6040
6041
		});
6042
6043
		// URL hasn't changed, next question please.
6044
		$( '.site-not-moved' ).click(function( e ) {
6045
			e.preventDefault();
6046
			$( '.jp-id-crisis-question' ).hide();
6047
			$( '#jp-id-crisis-question-2' ).show();
6048
		});
6049
6050
		// Reset connection: two separate sites.
6051
		$( '.reset-connection' ).click(function( e ) {
6052
			data.crisis_resolution_action = 'reset_connection';
6053
			$.post( ajaxurl, data, function( response ) {
6054
				if ( 'reset-connection-success' === response ) {
6055
					window.location.replace( '<?php echo Jetpack::admin_url(); ?>' );
6056
				}
6057
			});
6058
		});
6059
6060
		// It's a dev environment.  Ignore.
6061
		$( '.is-dev-env' ).click(function( e ) {
6062
			data.crisis_resolution_action = 'whitelist';
6063
			$( '#jp-id-crisis-question-2 .spinner' ).show();
6064
			$.post( ajaxurl, data, function() {
6065
				$( '.jp-id-crisis-question' ).hide();
6066
				$( '.banner-title' ).hide();
6067
				$( '#jp-id-crisis-success' ).show();
6068
				setTimeout( autodismissSuccessBanner, 4 * SECOND_IN_MS );
6069
			});
6070
		});
6071
6072
		$( '.not-reconnecting' ).click(contactSupport);
6073
		$( '.not-staging-or-dev' ).click(contactSupport);
6074
	});
6075
})( jQuery );
6076
</script>
6077
<?php
6078
	}
6079
6080
	/**
6081
	 * Displays an admin_notice, alerting the user to an identity crisis.
6082
	 */
6083
	public function alert_identity_crisis() {
6084
		// @todo temporary killing of feature in 3.8.1 as it revealed a number of scenarios not foreseen.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
6085
		if ( ! Jetpack::is_development_version() ) {
6086
			return;
6087
		}
6088
6089
		// @todo temporary copout for dealing with domain mapping
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
6090
		// @see https://github.com/Automattic/jetpack/issues/2702
6091
		if ( is_multisite() && defined( 'SUNRISE' ) && ! Jetpack::is_development_version() ) {
6092
			return;
6093
		}
6094
6095
		if ( ! current_user_can( 'jetpack_disconnect' ) ) {
6096
			return;
6097
		}
6098
6099
		if ( ! $errors = self::check_identity_crisis() ) {
6100
			return;
6101
		}
6102
6103
		// Only show on dashboard and jetpack pages
6104
		$screen = get_current_screen();
6105
		if ( 'dashboard' !== $screen->base && ! did_action( 'jetpack_notices' ) ) {
6106
			return;
6107
		}
6108
6109
		// Include the js!
6110
		$ajax_nonce = wp_create_nonce( 'resolve-identity-crisis' );
6111
		$this->identity_crisis_js( $ajax_nonce );
6112
6113
		// Include the CSS!
6114
		if ( ! wp_script_is( 'jetpack', 'done' ) ) {
6115
			$this->admin_banner_styles();
6116
		}
6117
6118
		if ( ! array_key_exists( 'error_code', $errors ) ) {
6119
			$key = 'siteurl';
6120
			if ( ! $errors[ $key ] ) {
6121
				$key = 'home';
6122
			}
6123
		} else {
6124
			$key = 'error_code';
6125
			// 401 is the only error we care about.  Any other errors should not trigger the alert.
6126
			if ( 401 !== $errors[ $key ] ) {
6127
				return;
6128
			}
6129
		}
6130
6131
		?>
6132
6133
		<style>
6134
			.jp-identity-crisis .jp-btn-group {
6135
					margin: 15px 0;
6136
				}
6137
			.jp-identity-crisis strong {
6138
					color: #518d2a;
6139
				}
6140
			.jp-identity-crisis.dismiss {
6141
				display: none;
6142
			}
6143
			.jp-identity-crisis .button {
6144
				margin-right: 4px;
6145
			}
6146
		</style>
6147
6148
		<div id="message" class="error jetpack-message jp-identity-crisis stay-visible">
6149
			<div class="service-mark"></div>
6150
			<div class="jp-id-banner__content">
6151
				<!-- <h3 class="banner-title"><?php _e( 'Something\'s not quite right with your Jetpack connection! Let\'s fix that.', 'jetpack' ); ?></h3> -->
6152
6153
				<div class="jp-id-crisis-question" id="jp-id-crisis-question-1">
6154
					<?php
6155
					// 401 means that this site has been disconnected from wpcom, but the remote site still thinks it's connected.
6156
					if ( 'error_code' == $key && '401' == $errors[ $key ] ) : ?>
6157
						<div class="banner-content">
6158
							<p><?php
6159
								/* translators: %s is a URL */
6160
								printf( __( 'Our records show that this site does not have a valid connection to WordPress.com. Please reset your connection to fix this. <a href="%s" target="_blank">What caused this?</a>', 'jetpack' ), 'https://jetpack.me/support/no-valid-wordpress-com-connection/' );
6161
							?></p>
6162
						</div>
6163
						<div class="jp-btn-group">
6164
							<a href="#" class="reset-connection"><?php _e( 'Reset the connection', 'jetpack' ); ?></a>
6165
							<span class="idc-separator">|</span>
6166
							<a href="<?php echo esc_url( wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=dismiss' ), 'jetpack-deactivate' ) ); ?>"><?php _e( 'Deactivate Jetpack', 'jetpack' ); ?></a>
6167
						</div>
6168
					<?php else : ?>
6169
							<div class="banner-content">
6170
							<p><?php printf( __( 'It looks like you may have changed your domain. Is <strong>%1$s</strong> still your site\'s domain, or have you updated it to <strong> %2$s </strong>?', 'jetpack' ), $errors[ $key ], (string) get_option( $key ) ); ?></p>
6171
							</div>
6172
						<div class="jp-btn-group">
6173
							<a href="#" class="regular site-moved"><?php printf( __( '%s is now my domain.', 'jetpack' ), $errors[ $key ] ); ?></a> <span class="idc-separator">|</span> <a href="#" class="site-not-moved" ><?php printf( __( '%s is still my domain.', 'jetpack' ), (string) get_option( $key ) ); ?></a>
6174
							<span class="spinner"></span>
6175
						</div>
6176
					<?php endif ; ?>
6177
				</div>
6178
6179
				<div class="jp-id-crisis-question" id="jp-id-crisis-question-2" style="display: none;">
6180
					<div class="banner-content">
6181
						<p><?php printf(
6182
							/* translators: %1$s, %2$s and %3$s are URLs */
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
6183
							__(
6184
								'Are <strong> %2$s </strong> and <strong> %1$s </strong> two completely separate websites? If so we should create a new connection, which will reset your followers and linked services. <a href="%3$s"><em>What does this mean?</em></a>',
6185
								'jetpack'
6186
							),
6187
							$errors[ $key ],
6188
							(string) get_option( $key ),
6189
							'https://jetpack.me/support/what-does-resetting-the-connection-mean/'
6190
						); ?></p>
6191
					</div>
6192
					<div class="jp-btn-group">
6193
						<a href="#" class="reset-connection"><?php _e( 'Reset the connection', 'jetpack' ); ?></a> <span class="idc-separator">|</span>
6194
						<a href="#" class="is-dev-env"><?php _e( 'This is a development environment', 'jetpack' ); ?></a> <span class="idc-separator">|</span>
6195
						<a href="https://jetpack.me/contact-support/" class="contact-support"><?php _e( 'Submit a support ticket', 'jetpack' ); ?></a>
6196
						<span class="spinner"></span>
6197
					</div>
6198
				</div>
6199
6200
				<div class="jp-id-crisis-success" id="jp-id-crisis-success" style="display: none;">
6201
					<h3 class="success-notice"><?php printf( __( 'Thanks for taking the time to sort things out. We&#039;ve updated our records accordingly!', 'jetpack' ) ); ?></h3>
6202
				</div>
6203
			</div>
6204
		</div>
6205
6206
		<?php
6207
	}
6208
6209
	/**
6210
	 * Maybe Use a .min.css stylesheet, maybe not.
6211
	 *
6212
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6213
	 */
6214
	public static function maybe_min_asset( $url, $path, $plugin ) {
6215
		// Short out on things trying to find actual paths.
6216
		if ( ! $path || empty( $plugin ) ) {
6217
			return $url;
6218
		}
6219
6220
		// Strip out the abspath.
6221
		$base = dirname( plugin_basename( $plugin ) );
6222
6223
		// Short out on non-Jetpack assets.
6224
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6225
			return $url;
6226
		}
6227
6228
		// File name parsing.
6229
		$file              = "{$base}/{$path}";
6230
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6231
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6232
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6233
		$extension         = array_shift( $file_name_parts_r );
6234
6235
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6236
			// Already pointing at the minified version.
6237
			if ( 'min' === $file_name_parts_r[0] ) {
6238
				return $url;
6239
			}
6240
6241
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6242
			if ( file_exists( $min_full_path ) ) {
6243
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6244
			}
6245
		}
6246
6247
		return $url;
6248
	}
6249
6250
	/**
6251
	 * Maybe inlines a stylesheet.
6252
	 *
6253
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6254
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6255
	 *
6256
	 * Attached to `style_loader_tag` filter.
6257
	 *
6258
	 * @param string $tag The tag that would link to the external asset.
6259
	 * @param string $handle The registered handle of the script in question.
6260
	 *
6261
	 * @return string
6262
	 */
6263
	public static function maybe_inline_style( $tag, $handle ) {
6264
		global $wp_styles;
6265
		$item = $wp_styles->registered[ $handle ];
6266
6267
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6268
			return $tag;
6269
		}
6270
6271
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6272
			$href = $matches[1];
6273
			// Strip off query string
6274
			if ( $pos = strpos( $href, '?' ) ) {
6275
				$href = substr( $href, 0, $pos );
6276
			}
6277
			// Strip off fragment
6278
			if ( $pos = strpos( $href, '#' ) ) {
6279
				$href = substr( $href, 0, $pos );
6280
			}
6281
		} else {
6282
			return $tag;
6283
		}
6284
6285
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6286
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6287
			return $tag;
6288
		}
6289
6290
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6291
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6292
			// And this isn't the pass that actually deals with the RTL version...
6293
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6294
				// Short out, as the RTL version will deal with it in a moment.
6295
				return $tag;
6296
			}
6297
		}
6298
6299
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6300
		$css  = Jetpack::absolutize_css_urls( file_get_contents( $file ), $href );
6301
		if ( $css ) {
6302
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6303
			if ( empty( $item->extra['after'] ) ) {
6304
				wp_add_inline_style( $handle, $css );
6305
			} else {
6306
				array_unshift( $item->extra['after'], $css );
6307
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6308
			}
6309
		}
6310
6311
		return $tag;
6312
	}
6313
6314
	/**
6315
	 * Loads a view file from the views
6316
	 *
6317
	 * Data passed in with the $data parameter will be available in the
6318
	 * template file as $data['value']
6319
	 *
6320
	 * @param string $template - Template file to load
6321
	 * @param array $data - Any data to pass along to the template
6322
	 * @return boolean - If template file was found
6323
	 **/
6324
	public function load_view( $template, $data = array() ) {
6325
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6326
6327
		if( file_exists( $views_dir . $template ) ) {
6328
			require_once( $views_dir . $template );
6329
			return true;
6330
		}
6331
6332
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6333
		return false;
6334
	}
6335
6336
	/**
6337
	 * Sends a ping to the Jetpack servers to toggle on/off remote portions
6338
	 * required by some modules.
6339
	 *
6340
	 * @param string $module_slug
6341
	 */
6342
	public function toggle_module_on_wpcom( $module_slug ) {
6343
		Jetpack::init()->sync->register( 'noop' );
6344
6345
		if ( false !== strpos( current_filter(), 'jetpack_activate_module_' ) ) {
6346
			self::check_privacy( $module_slug );
6347
		}
6348
6349
	}
6350
6351
	/**
6352
	 * Throws warnings for deprecated hooks to be removed from Jetpack
6353
	 */
6354
	public function deprecated_hooks() {
6355
		global $wp_filter;
6356
6357
		/*
6358
		 * Format:
6359
		 * deprecated_filter_name => replacement_name
6360
		 *
6361
		 * If there is no replacement us null for replacement_name
6362
		 */
6363
		$deprecated_list = array(
6364
			'jetpack_bail_on_shortcode' => 'jetpack_shortcodes_to_include',
6365
			'wpl_sharing_2014_1'        => null,
6366
		);
6367
6368
		// This is a silly loop depth. Better way?
6369
		foreach( $deprecated_list AS $hook => $hook_alt ) {
6370
			if( isset( $wp_filter[ $hook ] ) && is_array( $wp_filter[ $hook ] ) ) {
6371
				foreach( $wp_filter[$hook] AS $func => $values ) {
6372
					foreach( $values AS $hooked ) {
6373
						_deprecated_function( $hook . ' used for ' . $hooked['function'], null, $hook_alt );
6374
					}
6375
				}
6376
			}
6377
		}
6378
	}
6379
6380
	/**
6381
	 * Converts any url in a stylesheet, to the correct absolute url.
6382
	 *
6383
	 * Considerations:
6384
	 *  - Normal, relative URLs     `feh.png`
6385
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6386
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6387
	 *  - Absolute URLs             `http://domain.com/feh.png`
6388
	 *  - Domain root relative URLs `/feh.png`
6389
	 *
6390
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6391
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6392
	 *
6393
	 * @return mixed|string
6394
	 */
6395
	public static function absolutize_css_urls( $css, $css_file_url ) {
6396
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6397
		$css_dir = dirname( $css_file_url );
6398
		$p       = parse_url( $css_dir );
6399
		$domain  = sprintf(
6400
					'%1$s//%2$s%3$s%4$s',
6401
					isset( $p['scheme'] )           ? "{$p['scheme']}:" : '',
6402
					isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6403
					$p['host'],
6404
					isset( $p['port'] )             ? ":{$p['port']}" : ''
6405
				);
6406
6407
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6408
			$find = $replace = array();
6409
			foreach ( $matches as $match ) {
6410
				$url = trim( $match['path'], "'\" \t" );
6411
6412
				// If this is a data url, we don't want to mess with it.
6413
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6414
					continue;
6415
				}
6416
6417
				// If this is an absolute or protocol-agnostic url,
6418
				// we don't want to mess with it.
6419
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6420
					continue;
6421
				}
6422
6423
				switch ( substr( $url, 0, 1 ) ) {
6424
					case '/':
6425
						$absolute = $domain . $url;
6426
						break;
6427
					default:
6428
						$absolute = $css_dir . '/' . $url;
6429
				}
6430
6431
				$find[]    = $match[0];
6432
				$replace[] = sprintf( 'url("%s")', $absolute );
6433
			}
6434
			$css = str_replace( $find, $replace, $css );
6435
		}
6436
6437
		return $css;
6438
	}
6439
6440
	/**
6441
	 * This method checks to see if SSL is required by the site in
6442
	 * order to visit it in some way other than only setting the
6443
	 * https value in the home or siteurl values.
6444
	 *
6445
	 * @since 3.2
6446
	 * @return boolean
6447
	 **/
6448
	private function is_ssl_required_to_visit_site() {
6449
		global $wp_version;
6450
		$ssl = is_ssl();
6451
6452
		if ( version_compare( $wp_version, '4.4-alpha', '<=' ) && force_ssl_login() ) { // force_ssl_login deprecated WP 4.4.
6453
			$ssl = true;
6454
		} else if ( force_ssl_admin() ) {
6455
			$ssl = true;
6456
		}
6457
		return $ssl;
6458
	}
6459
6460
	/**
6461
	 * This methods removes all of the registered css files on the frontend
6462
	 * from Jetpack in favor of using a single file. In effect "imploding"
6463
	 * all the files into one file.
6464
	 *
6465
	 * Pros:
6466
	 * - Uses only ONE css asset connection instead of 15
6467
	 * - Saves a minimum of 56k
6468
	 * - Reduces server load
6469
	 * - Reduces time to first painted byte
6470
	 *
6471
	 * Cons:
6472
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6473
	 *		should not cause any issues with themes.
6474
	 * - Plugins/themes dequeuing styles no longer do anything. See
6475
	 *		jetpack_implode_frontend_css filter for a workaround
6476
	 *
6477
	 * For some situations developers may wish to disable css imploding and
6478
	 * instead operate in legacy mode where each file loads seperately and
6479
	 * can be edited individually or dequeued. This can be accomplished with
6480
	 * the following line:
6481
	 *
6482
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6483
	 *
6484
	 * @since 3.2
6485
	 **/
6486
	public function implode_frontend_css( $travis_test = false ) {
6487
		$do_implode = true;
6488
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6489
			$do_implode = false;
6490
		}
6491
6492
		/**
6493
		 * Allow CSS to be concatenated into a single jetpack.css file.
6494
		 *
6495
		 * @since 3.2.0
6496
		 *
6497
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6498
		 */
6499
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6500
6501
		// Do not use the imploded file when default behaviour was altered through the filter
6502
		if ( ! $do_implode ) {
6503
			return;
6504
		}
6505
6506
		// We do not want to use the imploded file in dev mode, or if not connected
6507
		if ( Jetpack::is_development_mode() || ! self::is_active() ) {
6508
			if ( ! $travis_test ) {
6509
				return;
6510
			}
6511
		}
6512
6513
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6514
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6515
			return;
6516
		}
6517
6518
		/*
6519
		 * Now we assume Jetpack is connected and able to serve the single
6520
		 * file.
6521
		 *
6522
		 * In the future there will be a check here to serve the file locally
6523
		 * or potentially from the Jetpack CDN
6524
		 *
6525
		 * For now:
6526
		 * - Enqueue a single imploded css file
6527
		 * - Zero out the style_loader_tag for the bundled ones
6528
		 * - Be happy, drink scotch
6529
		 */
6530
6531
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6532
6533
		$version = Jetpack::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6534
6535
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6536
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6537
	}
6538
6539
	function concat_remove_style_loader_tag( $tag, $handle ) {
6540
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6541
			$tag = '';
6542
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6543
				$tag = "<!-- `" . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6544
			}
6545
		}
6546
6547
		return $tag;
6548
	}
6549
6550
	/*
6551
	 * Check the heartbeat data
6552
	 *
6553
	 * Organizes the heartbeat data by severity.  For example, if the site
6554
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6555
	 *
6556
	 * Data will be added to "caution" array, if it either:
6557
	 *  - Out of date Jetpack version
6558
	 *  - Out of date WP version
6559
	 *  - Out of date PHP version
6560
	 *
6561
	 * $return array $filtered_data
6562
	 */
6563
	public static function jetpack_check_heartbeat_data() {
6564
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6565
6566
		$good    = array();
6567
		$caution = array();
6568
		$bad     = array();
6569
6570
		foreach ( $raw_data as $stat => $value ) {
6571
6572
			// Check jetpack version
6573
			if ( 'version' == $stat ) {
6574
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6575
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__VERSION;
6576
					continue;
6577
				}
6578
			}
6579
6580
			// Check WP version
6581
			if ( 'wp-version' == $stat ) {
6582
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6583
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_WP_VERSION;
6584
					continue;
6585
				}
6586
			}
6587
6588
			// Check PHP version
6589
			if ( 'php-version' == $stat ) {
6590
				if ( version_compare( PHP_VERSION, '5.2.4', '<' ) ) {
6591
					$caution[ $stat ] = $value . " - min supported is 5.2.4";
6592
					continue;
6593
				}
6594
			}
6595
6596
			// Check ID crisis
6597
			if ( 'identitycrisis' == $stat ) {
6598
				if ( 'yes' == $value ) {
6599
					$bad[ $stat ] = $value;
6600
					continue;
6601
				}
6602
			}
6603
6604
			// The rest are good :)
6605
			$good[ $stat ] = $value;
6606
		}
6607
6608
		$filtered_data = array(
6609
			'good'    => $good,
6610
			'caution' => $caution,
6611
			'bad'     => $bad
6612
		);
6613
6614
		return $filtered_data;
6615
	}
6616
6617
6618
	/*
6619
	 * This method is used to organize all options that can be reset
6620
	 * without disconnecting Jetpack.
6621
	 *
6622
	 * It is used in class.jetpack-cli.php to reset options
6623
	 *
6624
	 * @return array of options to delete.
6625
	 */
6626
	public static function get_jetpack_options_for_reset() {
6627
		$jetpack_options            = Jetpack_Options::get_option_names();
6628
		$jetpack_options_non_compat = Jetpack_Options::get_option_names( 'non_compact' );
6629
		$jetpack_options_private    = Jetpack_Options::get_option_names( 'private' );
6630
6631
		$all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
6632
6633
		// A manual build of the wp options
6634
		$wp_options = array(
6635
			'sharing-options',
6636
			'disabled_likes',
6637
			'disabled_reblogs',
6638
			'jetpack_comments_likes_enabled',
6639
			'wp_mobile_excerpt',
6640
			'wp_mobile_featured_images',
6641
			'wp_mobile_app_promos',
6642
			'stats_options',
6643
			'stats_dashboard_widget',
6644
			'safecss_preview_rev',
6645
			'safecss_rev',
6646
			'safecss_revision_migrated',
6647
			'nova_menu_order',
6648
			'jetpack_portfolio',
6649
			'jetpack_portfolio_posts_per_page',
6650
			'jetpack_testimonial',
6651
			'jetpack_testimonial_posts_per_page',
6652
			'wp_mobile_custom_css',
6653
			'sharedaddy_disable_resources',
6654
			'sharing-options',
6655
			'sharing-services',
6656
			'site_icon_temp_data',
6657
			'featured-content',
6658
			'site_logo',
6659
		);
6660
6661
		// Flag some Jetpack options as unsafe
6662
		$unsafe_options = array(
6663
			'id',                           // (int)    The Client ID/WP.com Blog ID of this site.
6664
			'master_user',                  // (int)    The local User ID of the user who connected this site to jetpack.wordpress.com.
6665
			'version',                      // (string) Used during upgrade procedure to auto-activate new modules. version:time
6666
			'jumpstart',                    // (string) A flag for whether or not to show the Jump Start.  Accepts: new_connection, jumpstart_activated, jetpack_action_taken, jumpstart_dismissed.
6667
6668
			// non_compact
6669
			'activated',
6670
6671
			// private
6672
			'register',
6673
			'blog_token',                  // (string) The Client Secret/Blog Token of this site.
6674
			'user_token',                  // (string) The User Token of this site. (deprecated)
6675
			'user_tokens'
6676
		);
6677
6678
		// Remove the unsafe Jetpack options
6679
		foreach ( $unsafe_options as $unsafe_option ) {
6680
			if ( false !== ( $key = array_search( $unsafe_option, $all_jp_options ) ) ) {
6681
				unset( $all_jp_options[ $key ] );
6682
			}
6683
		}
6684
6685
		$options = array(
6686
			'jp_options' => $all_jp_options,
6687
			'wp_options' => $wp_options
6688
		);
6689
6690
		return $options;
6691
	}
6692
6693
	/*
6694
	 * Check if an option of a Jetpack module has been updated.
6695
	 *
6696
	 * If any module option has been updated before Jump Start has been dismissed,
6697
	 * update the 'jumpstart' option so we can hide Jump Start.
6698
	 */
6699
	public static function jumpstart_has_updated_module_option( $option_name = '' ) {
6700
		// Bail if Jump Start has already been dismissed
6701
		if ( 'new_connection' !== Jetpack::get_option( 'jumpstart' ) ) {
6702
			return false;
6703
		}
6704
6705
		$jetpack = Jetpack::init();
6706
6707
6708
		// Manual build of module options
6709
		$option_names = self::get_jetpack_options_for_reset();
6710
6711
		if ( in_array( $option_name, $option_names['wp_options'] ) ) {
6712
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
6713
6714
			//Jump start is being dismissed send data to MC Stats
6715
			$jetpack->stat( 'jumpstart', 'manual,'.$option_name );
6716
6717
			$jetpack->do_stats( 'server_side' );
6718
		}
6719
6720
	}
6721
6722
	/*
6723
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6724
	 * so we can bring them directly to their site in calypso.
6725
	 *
6726
	 * @param string | url
6727
	 * @return string | url without the guff
6728
	 */
6729
	public static function build_raw_urls( $url ) {
6730
		$strip_http = '/.*?:\/\//i';
6731
		$url = preg_replace( $strip_http, '', $url  );
6732
		$url = str_replace( '/', '::', $url );
6733
		return $url;
6734
	}
6735
6736
	/**
6737
	 * Stores and prints out domains to prefetch for page speed optimization.
6738
	 *
6739
	 * @param mixed $new_urls
6740
	 */
6741
	public static function dns_prefetch( $new_urls = null ) {
6742
		static $prefetch_urls = array();
6743
		if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) {
6744
			echo "\r\n";
6745
			foreach ( $prefetch_urls as $this_prefetch_url ) {
6746
				printf( "<link rel='dns-prefetch' href='%s'>\r\n", esc_attr( $this_prefetch_url ) );
6747
			}
6748
		} elseif ( ! empty( $new_urls ) ) {
6749
			if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) {
6750
				add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) );
6751
			}
6752
			foreach ( (array) $new_urls as $this_new_url ) {
6753
				$prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) );
6754
			}
6755
			$prefetch_urls = array_unique( $prefetch_urls );
6756
		}
6757
	}
6758
6759
	public function wp_dashboard_setup() {
6760
		if ( self::is_active() ) {
6761
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6762
			$widget_title = __( 'Site Stats', 'jetpack' );
6763
		} elseif ( ! self::is_development_mode() && current_user_can( 'jetpack_connect' ) ) {
6764
			add_action( 'jetpack_dashboard_widget', array( $this, 'dashboard_widget_connect_to_wpcom' ) );
6765
			$widget_title = __( 'Please Connect Jetpack', 'jetpack' );
6766
		}
6767
6768
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6769
			wp_add_dashboard_widget(
6770
				'jetpack_summary_widget',
6771
				$widget_title,
0 ignored issues
show
Bug introduced by
The variable $widget_title 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...
6772
				array( __CLASS__, 'dashboard_widget' )
6773
			);
6774
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6775
6776
			// If we're inactive and not in development mode, sort our box to the top.
6777
			if ( ! self::is_active() && ! self::is_development_mode() ) {
6778
				global $wp_meta_boxes;
6779
6780
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6781
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6782
6783
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6784
			}
6785
		}
6786
	}
6787
6788
	/**
6789
	 * @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...
6790
	 * @return mixed
6791
	 */
6792
	function get_user_option_meta_box_order_dashboard( $sorted ) {
6793
		if ( ! is_array( $sorted ) ) {
6794
			return $sorted;
6795
		}
6796
6797
		foreach ( $sorted as $box_context => $ids ) {
6798
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6799
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6800
				continue;
6801
			}
6802
6803
			$ids_array = explode( ',', $ids );
6804
			$key = array_search( 'dashboard_stats', $ids_array );
6805
6806
			if ( false !== $key ) {
6807
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
6808
				$ids_array[ $key ] = 'jetpack_summary_widget';
6809
				$sorted[ $box_context ] = implode( ',', $ids_array );
6810
				// We've found it, stop searching, and just return.
6811
				break;
6812
			}
6813
		}
6814
6815
		return $sorted;
6816
	}
6817
6818
	public static function dashboard_widget() {
6819
		/**
6820
		 * Fires when the dashboard is loaded.
6821
		 *
6822
		 * @since 3.4.0
6823
		 */
6824
		do_action( 'jetpack_dashboard_widget' );
6825
	}
6826
6827
	public static function dashboard_widget_footer() {
6828
		?>
6829
		<footer>
6830
6831
		<div class="protect">
6832
			<?php if ( Jetpack::is_module_active( 'protect' ) ) : ?>
6833
				<h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3>
6834
				<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>
6835
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! self::is_development_mode() ) : ?>
6836
				<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' ); ?>">
6837
					<?php esc_html_e( 'Activate Protect', 'jetpack' ); ?>
6838
				</a>
6839
			<?php else : ?>
6840
				<?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?>
6841
			<?php endif; ?>
6842
		</div>
6843
6844
		<div class="akismet">
6845
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
6846
				<h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3>
6847
				<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>
6848 View Code Duplication
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
6849
				<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">
6850
					<?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?>
6851
				</a>
6852
			<?php else : ?>
6853
				<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>
6854
			<?php endif; ?>
6855
		</div>
6856
6857
6858 View Code Duplication
		<?php if ( ! current_user_can( 'edit_posts' ) && self::is_user_connected() ) : ?>
6859
			<div style="width: 100%; text-align: center; padding-top: 20px; clear: both;"><a class="button" title="<?php esc_attr_e( 'Unlink your account from WordPress.com', 'jetpack' ); ?>" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'unlink', 'redirect' => 'sub-unlink' ), admin_url( 'index.php' ) ), 'jetpack-unlink' ) ); ?>"><?php esc_html_e( 'Unlink your account from WordPress.com', 'jetpack' ); ?></a></div>
6860
		<?php endif; ?>
6861
6862
		</footer>
6863
		<?php
6864
	}
6865
6866
	public function dashboard_widget_connect_to_wpcom() {
6867
		if ( Jetpack::is_active() || Jetpack::is_development_mode() || ! current_user_can( 'jetpack_connect' ) ) {
6868
			return;
6869
		}
6870
		?>
6871
		<div class="wpcom-connect">
6872
			<div class="jp-emblem">
6873
			<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0" y="0" viewBox="0 0 172.9 172.9" enable-background="new 0 0 172.9 172.9" xml:space="preserve">
6874
				<path d="M86.4 0C38.7 0 0 38.7 0 86.4c0 47.7 38.7 86.4 86.4 86.4s86.4-38.7 86.4-86.4C172.9 38.7 134.2 0 86.4 0zM83.1 106.6l-27.1-6.9C49 98 45.7 90.1 49.3 84l33.8-58.5V106.6zM124.9 88.9l-33.8 58.5V66.3l27.1 6.9C125.1 74.9 128.4 82.8 124.9 88.9z"/>
6875
			</svg>
6876
			</div>
6877
			<h3><?php esc_html_e( 'Please Connect Jetpack', 'jetpack' ); ?></h3>
6878
			<p><?php echo wp_kses( __( 'Connecting Jetpack will show you <strong>stats</strong> about your traffic, <strong>protect</strong> you from brute force attacks, <strong>speed up</strong> your images and photos, and enable other <strong>traffic and security</strong> features.', 'jetpack' ), 'jetpack' ) ?></p>
6879
6880
			<div class="actions">
6881
				<a href="<?php echo $this->build_connect_url() ?>" class="button button-primary">
6882
					<?php esc_html_e( 'Connect Jetpack', 'jetpack' ); ?>
6883
				</a>
6884
			</div>
6885
		</div>
6886
		<?php
6887
	}
6888
6889
	/*
6890
	 * A graceful transition to using Core's site icon.
6891
	 *
6892
	 * All of the hard work has already been done with the image
6893
	 * in all_done_page(). All that needs to be done now is update
6894
	 * the option and display proper messaging.
6895
	 *
6896
	 * @todo remove when WP 4.3 is minimum
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
6897
	 *
6898
	 * @since 3.6.1
6899
	 *
6900
	 * @return bool false = Core's icon not available || true = Core's icon is available
6901
	 */
6902
	public static function jetpack_site_icon_available_in_core() {
6903
		global $wp_version;
6904
		$core_icon_available = function_exists( 'has_site_icon' ) && version_compare( $wp_version, '4.3-beta' ) >= 0;
6905
6906
		if ( ! $core_icon_available ) {
6907
			return false;
6908
		}
6909
6910
		// No need for Jetpack's site icon anymore if core's is already set
6911
		if ( has_site_icon() ) {
6912
			if ( Jetpack::is_module_active( 'site-icon' ) ) {
6913
				Jetpack::log( 'deactivate', 'site-icon' );
6914
				Jetpack::deactivate_module( 'site-icon' );
6915
			}
6916
			return true;
6917
		}
6918
6919
		// Transfer Jetpack's site icon to use core.
6920
		$site_icon_id = Jetpack::get_option( 'site_icon_id' );
6921
		if ( $site_icon_id ) {
6922
			// Update core's site icon
6923
			update_option( 'site_icon', $site_icon_id );
6924
6925
			// Delete Jetpack's icon option. We still want the blavatar and attached data though.
6926
			delete_option( 'site_icon_id' );
6927
		}
6928
6929
		// No need for Jetpack's site icon anymore
6930
		if ( Jetpack::is_module_active( 'site-icon' ) ) {
6931
			Jetpack::log( 'deactivate', 'site-icon' );
6932
			Jetpack::deactivate_module( 'site-icon' );
6933
		}
6934
6935
		return true;
6936
	}
6937
6938
}
6939