Completed
Push — add/sync-rest-2 ( 1be3a0...df1b9f )
by
unknown
18:45 queued 09:48
created

Jetpack::jetpack_jitm_ajax_callback()   C

Complexity

Conditions 12
Paths 48

Size

Total Lines 57
Code Lines 33

Duplication

Lines 18
Ratio 31.58 %
Metric Value
dl 18
loc 57
rs 6.62
cc 12
eloc 33
nc 48
nop 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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']
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
		'jetpack_social_media_icons_widget',
53
	);
54
55
	public $plugins_to_deactivate = array(
56
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
57
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
58
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
59
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
60
		'after-the-deadline'  => array( 'after-the-deadline/after-the-deadline.php', 'After The Deadline' ),
61
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
62
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
63
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
64
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
65
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
66
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
67
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
68
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
69
		'omnisearch'          => array( 'jetpack-omnisearch/omnisearch.php', 'Jetpack Omnisearch' ),
70
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
71
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' )
72
	);
73
74
	static $capability_translations = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $capability_translations.

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...
75
		'administrator' => 'manage_options',
76
		'editor'        => 'edit_others_posts',
77
		'author'        => 'publish_posts',
78
		'contributor'   => 'edit_posts',
79
		'subscriber'    => 'read',
80
	);
81
82
	/**
83
	 * Map of modules that have conflicts with plugins and should not be auto-activated
84
	 * if the plugins are active.  Used by filter_default_modules
85
	 *
86
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
87
	 * change `module-slug` and add this to your plugin:
88
	 *
89
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
90
	 * function my_jetpack_get_default_modules( $modules ) {
91
	 *     return array_diff( $modules, array( 'module-slug' ) );
92
	 * }
93
	 *
94
	 * @var array
95
	 */
96
	private $conflicting_plugins = array(
97
		'comments'          => array(
98
			'Intense Debate'                       => 'intensedebate/intensedebate.php',
99
			'Disqus'                               => 'disqus-comment-system/disqus.php',
100
			'Livefyre'                             => 'livefyre-comments/livefyre.php',
101
			'Comments Evolved for WordPress'       => 'gplus-comments/comments-evolved.php',
102
			'Google+ Comments'                     => 'google-plus-comments/google-plus-comments.php',
103
			'WP-SpamShield Anti-Spam'              => 'wp-spamshield/wp-spamshield.php',
104
		),
105
		'contact-form'      => array(
106
			'Contact Form 7'                       => 'contact-form-7/wp-contact-form-7.php',
107
			'Gravity Forms'                        => 'gravityforms/gravityforms.php',
108
			'Contact Form Plugin'                  => 'contact-form-plugin/contact_form.php',
109
			'Easy Contact Forms'                   => 'easy-contact-forms/easy-contact-forms.php',
110
			'Fast Secure Contact Form'             => 'si-contact-form/si-contact-form.php',
111
		),
112
		'minileven'         => array(
113
			'WPtouch'                              => 'wptouch/wptouch.php',
114
		),
115
		'latex'             => array(
116
			'LaTeX for WordPress'                  => 'latex/latex.php',
117
			'Youngwhans Simple Latex'              => 'youngwhans-simple-latex/yw-latex.php',
118
			'Easy WP LaTeX'                        => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
119
			'MathJax-LaTeX'                        => 'mathjax-latex/mathjax-latex.php',
120
			'Enable Latex'                         => 'enable-latex/enable-latex.php',
121
			'WP QuickLaTeX'                        => 'wp-quicklatex/wp-quicklatex.php',
122
		),
123
		'protect'           => array(
124
			'Limit Login Attempts'                 => 'limit-login-attempts/limit-login-attempts.php',
125
			'Captcha'                              => 'captcha/captcha.php',
126
			'Brute Force Login Protection'         => 'brute-force-login-protection/brute-force-login-protection.php',
127
			'Login Security Solution'              => 'login-security-solution/login-security-solution.php',
128
			'WPSecureOps Brute Force Protect'      => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
129
			'BulletProof Security'                 => 'bulletproof-security/bulletproof-security.php',
130
			'SiteGuard WP Plugin'                  => 'siteguard/siteguard.php',
131
			'Security-protection'                  => 'security-protection/security-protection.php',
132
			'Login Security'                       => 'login-security/login-security.php',
133
			'Botnet Attack Blocker'                => 'botnet-attack-blocker/botnet-attack-blocker.php',
134
			'Wordfence Security'                   => 'wordfence/wordfence.php',
135
			'All In One WP Security & Firewall'    => 'all-in-one-wp-security-and-firewall/wp-security.php',
136
			'iThemes Security'                     => 'better-wp-security/better-wp-security.php',
137
		),
138
		'random-redirect'   => array(
139
			'Random Redirect 2'                    => 'random-redirect-2/random-redirect.php',
140
		),
141
		'related-posts'     => array(
142
			'YARPP'                                => 'yet-another-related-posts-plugin/yarpp.php',
143
			'WordPress Related Posts'              => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
144
			'nrelate Related Content'              => 'nrelate-related-content/nrelate-related.php',
145
			'Contextual Related Posts'             => 'contextual-related-posts/contextual-related-posts.php',
146
			'Related Posts for WordPress'          => 'microkids-related-posts/microkids-related-posts.php',
147
			'outbrain'                             => 'outbrain/outbrain.php',
148
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
149
			'Sexybookmarks'                        => 'sexybookmarks/shareaholic.php',
150
		),
151
		'sharedaddy'        => array(
152
			'AddThis'                              => 'addthis/addthis_social_widget.php',
153
			'Add To Any'                           => 'add-to-any/add-to-any.php',
154
			'ShareThis'                            => 'share-this/sharethis.php',
155
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
156
		),
157
		'verification-tools' => array(
158
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
159
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
160
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
161
		),
162
		'widget-visibility' => array(
163
			'Widget Logic'                         => 'widget-logic/widget_logic.php',
164
			'Dynamic Widgets'                      => 'dynamic-widgets/dynamic-widgets.php',
165
		),
166
		'sitemaps' => array(
167
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
168
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
169
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
170
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
171
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
172
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
173
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
174
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
175
			'Sitemap'                              => 'sitemap/sitemap.php',
176
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
177
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
178
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
179
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
180
		),
181
	);
182
183
	/**
184
	 * Plugins for which we turn off our Facebook OG Tags implementation.
185
	 *
186
	 * Note: WordPress SEO by Yoast and WordPress SEO Premium by Yoast automatically deactivate
187
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
188
	 *
189
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
190
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
191
	 */
192
	private $open_graph_conflicting_plugins = array(
193
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
194
		                                                         // 2 Click Social Media Buttons
195
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
196
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
197
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
198
		'facebook/facebook.php',                                 // Facebook (official plugin)
199
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
200
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
201
		                                                         // Facebook Featured Image & OG Meta Tags
202
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
203
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
204
		                                                         // Facebook Open Graph Meta Tags for WordPress
205
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
206
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
207
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
208
		                                                         // Fedmich's Facebook Open Graph Meta
209
		'header-footer/plugin.php',                              // Header and Footer
210
		'network-publisher/networkpub.php',                      // Network Publisher
211
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
212
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
213
		                                                         // NextScripts SNAP
214
		'opengraph/opengraph.php',                               // Open Graph
215
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
216
		                                                         // Open Graph Protocol Framework
217
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
218
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
219
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
220
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
221
		'sharepress/sharepress.php',                             // SharePress
222
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
223
		'social-discussions/social-discussions.php',             // Social Discussions
224
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
225
		'socialize/socialize.php',                               // Socialize
226
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
227
		                                                         // Tweet, Like, Google +1 and Share
228
		'wordbooker/wordbooker.php',                             // Wordbooker
229
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
230
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
231
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
232
		                                                         // WP Facebook Like Send & Open Graph Meta
233
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
234
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
235
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
236
		'wp-fb-share-like-button/wp_fb_share-like_widget.php'    // WP Facebook Like Button
237
	);
238
239
	/**
240
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
241
	 */
242
	private $twitter_cards_conflicting_plugins = array(
243
	//	'twitter/twitter.php',                       // The official one handles this on its own.
244
	//	                                             // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
245
		'eewee-twitter-card/index.php',              // Eewee Twitter Card
246
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
247
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
248
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
249
		                                             // Pure Web Brilliant's Social Graph Twitter Cards Extension
250
		'twitter-cards/twitter-cards.php',           // Twitter Cards
251
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
252
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
253
	);
254
255
	/**
256
	 * Message to display in admin_notice
257
	 * @var string
258
	 */
259
	public $message = '';
260
261
	/**
262
	 * Error to display in admin_notice
263
	 * @var string
264
	 */
265
	public $error = '';
266
267
	/**
268
	 * Modules that need more privacy description.
269
	 * @var string
270
	 */
271
	public $privacy_checks = '';
272
273
	/**
274
	 * Stats to record once the page loads
275
	 *
276
	 * @var array
277
	 */
278
	public $stats = array();
279
280
	/**
281
	 * Allows us to build a temporary security report
282
	 *
283
	 * @var array
284
	 */
285
	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...
286
287
	/**
288
	 * Jetpack_Sync object
289
	 */
290
	public $sync;
291
292
	/**
293
	 * Verified data for JSON authorization request
294
	 */
295
	public $json_api_authorization_request = array();
296
297
	/**
298
	 * Holds the singleton instance of this class
299
	 * @since 2.3.3
300
	 * @var Jetpack
301
	 */
302
	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...
303
304
	/**
305
	 * Singleton
306
	 * @static
307
	 */
308
	public static function init() {
309
		if ( ! self::$instance ) {
310
			if ( did_action( 'plugins_loaded' ) )
311
				self::plugin_textdomain();
312
			else
313
				add_action( 'plugins_loaded', array( __CLASS__, 'plugin_textdomain' ), 99 );
314
315
			self::$instance = new Jetpack;
316
317
			self::$instance->plugin_upgrade();
318
319
			add_action( 'init', array( __CLASS__, 'perform_security_reporting' ) );
320
321
		}
322
323
		return self::$instance;
324
	}
325
326
	/**
327
	 * Must never be called statically
328
	 */
329
	function plugin_upgrade() {
330
		if ( Jetpack::is_active() ) {
331
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
332
			if ( JETPACK__VERSION != $version ) {
333
334
				// Check which active modules actually exist and remove others from active_modules list
335
				$unfiltered_modules = Jetpack::get_active_modules();
336
				$modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
337
				if ( array_diff( $unfiltered_modules, $modules ) ) {
338
					Jetpack_Options::update_option( 'active_modules', $modules );
339
				}
340
341
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
342
				/**
343
				 * Fires when synchronizing all registered options and constants.
344
				 *
345
				 * @since 3.3.0
346
				 */
347
				do_action( 'jetpack_sync_all_registered_options' );
348
			}
349
		}
350
	}
351
352
	static function activate_manage( ) {
353
354
		if ( did_action( 'init' ) || current_filter() == 'init' ) {
355
			self::activate_module( 'manage', false, false );
356
		} else if ( !  has_action( 'init' , array( __CLASS__, 'activate_manage' ) ) ) {
357
			add_action( 'init', array( __CLASS__, 'activate_manage' ) );
358
		}
359
360
	}
361
362
	/**
363
	 * Constructor.  Initializes WordPress hooks
364
	 */
365
	private function __construct() {
366
		/*
367
		 * Check for and alert any deprecated hooks
368
		 */
369
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
370
371
		/*
372
		 * Do things that should run even in the network admin
373
		 * here, before we potentially fail out.
374
		 */
375
		add_filter( 'jetpack_require_lib_dir', array( $this, 'require_lib_dir' ) );
376
377
		/*
378
		 * Load things that should only be in Network Admin.
379
		 *
380
		 * For now blow away everything else until a more full
381
		 * understanding of what is needed at the network level is
382
		 * available
383
		 */
384
		if( is_multisite() ) {
385
			Jetpack_Network::init();
386
			if( is_network_admin() ) {
387
				return; // End here to prevent single site actions from firing
388
			}
389
		}
390
391
		/**
392
		 * Trigger an update to the main_network_site when we update the blogname of a site.
393
		 *
394
		 */
395
		add_action( 'update_option_siteurl', array( $this, 'update_jetpack_main_network_site_option' ) );
396
397
		add_action( 'update_option', array( $this, 'log_settings_change' ), 10, 3 );
398
399
		// Update the settings everytime the we register a new user to the site or we delete a user.
400
		add_action( 'user_register', array( $this, 'is_single_user_site_invalidate' ) );
401
		add_action( 'deleted_user', array( $this, 'is_single_user_site_invalidate' ) );
402
403
		// Unlink user before deleting the user from .com
404
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
405
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
406
407
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
408
			@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...
409
410
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
411
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
412
413
			$this->require_jetpack_authentication();
414
415
			if ( Jetpack::is_active() ) {
416
				// Hack to preserve $HTTP_RAW_POST_DATA
417
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
418
419
				$signed = $this->verify_xml_rpc_signature();
420
				if ( $signed && ! is_wp_error( $signed ) ) {
421
					// The actual API methods.
422
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
423
				} else {
424
					add_filter( 'xmlrpc_methods', '__return_empty_array' );
425
				}
426
			} else {
427
				// The bootstrap API methods.
428
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
429
			}
430
431
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
432
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
433
		} elseif ( is_admin() && isset( $_POST['action'] ) && 'jetpack_upload_file' == $_POST['action'] ) {
434
			$this->require_jetpack_authentication();
435
			$this->add_remote_request_handlers();
436
		} else {
437
			if ( Jetpack::is_active() ) {
438
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
439
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
440
			}
441
		}
442
443
		if ( Jetpack::is_active() ) {
444
			Jetpack_Heartbeat::init();
445
		}
446
447
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
448
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
449
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
450
		}
451
452
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
453
454
		add_action( 'admin_init', array( $this, 'admin_init' ) );
455
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
456
457
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
458
459
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
460
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
461
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
462
463
		// Jump Start AJAX callback function
464
		add_action( 'wp_ajax_jetpack_jumpstart_ajax',  array( $this, 'jetpack_jumpstart_ajax_callback' ) );
465
		add_action( 'update_option', array( $this, 'jumpstart_has_updated_module_option' ) );
466
467
		// Identity Crisis AJAX callback function
468
		add_action( 'wp_ajax_jetpack_resolve_identity_crisis', array( $this, 'resolve_identity_crisis_ajax_callback' ) );
469
470
		// JITM AJAX callback function
471
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
472
473
		add_action( 'wp_ajax_jetpack_admin_ajax',          array( $this, 'jetpack_admin_ajax_callback' ) );
474
		add_action( 'wp_ajax_jetpack_admin_ajax_refresh',  array( $this, 'jetpack_admin_ajax_refresh_data' ) );
475
476
		// Universal ajax callback for all tracking events triggered via js
477
		add_action( 'wp_ajax_jetpack_tracks', array( $this, 'jetpack_admin_ajax_tracks_callback' ) );
478
479
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
480
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
481
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
482
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
483
484
		add_action( 'jetpack_activate_module', array( $this, 'activate_module_actions' ) );
485
486
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
487
488
		add_action( 'jetpack_notices', array( $this, 'show_development_mode_notice' ) );
489
490
		/**
491
		 * These actions run checks to load additional files.
492
		 * They check for external files or plugins, so they need to run as late as possible.
493
		 */
494
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
495
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
496
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
497
498
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
499
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
500
501
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
502
503
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
504
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
505
506
		// A filter to control all just in time messages
507
		add_filter( 'jetpack_just_in_time_msgs', '__return_true' );
508
509
		/**
510
		 * This is the hack to concatinate all css files into one.
511
		 * For description and reasoning see the implode_frontend_css method
512
		 *
513
		 * Super late priority so we catch all the registered styles
514
		 */
515
		if( !is_admin() ) {
516
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
517
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
518
		}
519
520
	}
521
522
	function jetpack_admin_ajax_tracks_callback() {
523
		// Check for nonce
524
		if ( ! isset( $_REQUEST['tracksNonce'] ) || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' ) ) {
525
			wp_die( 'Permissions check failed.' );
526
		}
527
528
		if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] )  ) {
529
			wp_die( 'No valid event name or type.' );
530
		}
531
532
		$tracks_data = array();
533
		if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
534
			$tracks_data = array( 'clicked' => $_REQUEST['tracksEventProp'] );
535
		}
536
537
		JetpackTracking::record_user_event( $_REQUEST['tracksEventName'], $tracks_data );
538
		wp_send_json_success();
539
		wp_die();
540
	}
541
542
	function jetpack_admin_ajax_callback() {
543
		// Check for nonce
544 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) || ! current_user_can( 'jetpack_manage_modules' ) ) {
545
			wp_die( 'permissions check failed' );
546
		}
547
548
		if ( isset( $_REQUEST['toggleModule'] ) && 'nux-toggle-module' == $_REQUEST['toggleModule'] ) {
549
			$slug = $_REQUEST['thisModuleSlug'];
550
551
			if ( ! in_array( $slug, Jetpack::get_available_modules() ) ) {
552
				wp_die( 'That is not a Jetpack module slug' );
553
			}
554
555
			if ( Jetpack::is_module_active( $slug ) ) {
556
				Jetpack::deactivate_module( $slug );
557
			} else {
558
				Jetpack::activate_module( $slug, false, false );
559
			}
560
561
			$modules = Jetpack_Admin::init()->get_modules();
562
			echo json_encode( $modules[ $slug ] );
563
564
			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...
565
		}
566
567
		wp_die();
568
	}
569
570
	/*
571
	 * Sometimes we need to refresh the data,
572
	 * especially if the page is visited via a 'history'
573
	 * event like back/forward
574
	 */
575
	function jetpack_admin_ajax_refresh_data() {
576
		// Check for nonce
577 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) ) {
578
			wp_die( 'permissions check failed' );
579
		}
580
581
		if ( isset( $_REQUEST['refreshData'] ) && 'refresh' == $_REQUEST['refreshData'] ) {
582
			$modules = Jetpack_Admin::init()->get_modules();
583
			echo json_encode( $modules );
584
			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...
585
		}
586
587
		wp_die();
588
	}
589
590
	/**
591
	 * The callback for the Jump Start ajax requests.
592
	 */
593
	function jetpack_jumpstart_ajax_callback() {
594
		// Check for nonce
595
		if ( ! isset( $_REQUEST['jumpstartNonce'] ) || ! wp_verify_nonce( $_REQUEST['jumpstartNonce'], 'jetpack-jumpstart-nonce' ) )
596
			wp_die( 'permissions check failed' );
597
598
		if ( isset( $_REQUEST['jumpStartActivate'] ) && 'jump-start-activate' == $_REQUEST['jumpStartActivate'] ) {
599
			// Update the jumpstart option
600
			if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
601
				Jetpack_Options::update_option( 'jumpstart', 'jumpstart_activated' );
602
			}
603
604
			// Loops through the requested "Jump Start" modules, and activates them.
605
			// Custom 'no_message' state, so that no message will be shown on reload.
606
			$modules = $_REQUEST['jumpstartModSlug'];
607
			$module_slugs = array();
608
			foreach( $modules as $module => $value ) {
609
				$module_slugs[] = $value['module_slug'];
610
			}
611
612
			// Check for possible conflicting plugins
613
			$module_slugs_filtered = $this->filter_default_modules( $module_slugs );
614
615
			foreach ( $module_slugs_filtered as $module_slug ) {
616
				Jetpack::log( 'activate', $module_slug );
617
				Jetpack::activate_module( $module_slug, false, false );
618
				Jetpack::state( 'message', 'no_message' );
619
			}
620
621
			// Set the default sharing buttons and set to display on posts if none have been set.
622
			$sharing_services = get_option( 'sharing-services' );
623
			$sharing_options  = get_option( 'sharing-options' );
624
			if ( empty( $sharing_services['visible'] ) ) {
625
				// Default buttons to set
626
				$visible = array(
627
					'twitter',
628
					'facebook',
629
					'google-plus-1',
630
				);
631
				$hidden = array();
632
633
				// Set some sharing settings
634
				$sharing = new Sharing_Service();
635
				$sharing_options['global'] = array(
636
					'button_style'  => 'icon',
637
					'sharing_label' => $sharing->default_sharing_label,
638
					'open_links'    => 'same',
639
					'show'          => array( 'post' ),
640
					'custom'        => isset( $sharing_options['global']['custom'] ) ? $sharing_options['global']['custom'] : array()
641
				);
642
643
				update_option( 'sharing-options', $sharing_options );
644
645
				// Send a success response so that we can display an error message.
646
				$success = update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) );
647
				echo json_encode( $success );
648
				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...
649
			}
650
651
		} elseif ( isset( $_REQUEST['disableJumpStart'] ) && true == $_REQUEST['disableJumpStart'] ) {
652
			// If dismissed, flag the jumpstart option as such.
653
			// Send a success response so that we can display an error message.
654
			if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
655
				$success = Jetpack_Options::update_option( 'jumpstart', 'jumpstart_dismissed' );
656
				echo json_encode( $success );
657
				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...
658
			}
659
660
		} elseif ( isset( $_REQUEST['jumpStartDeactivate'] ) && 'jump-start-deactivate' == $_REQUEST['jumpStartDeactivate'] ) {
661
662
			// FOR TESTING ONLY
663
			// @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...
664
			$modules = (array) $_REQUEST['jumpstartModSlug'];
665
			foreach( $modules as $module => $value ) {
666
				if ( !in_array( $value['module_slug'], Jetpack::get_default_modules() ) ) {
667
					Jetpack::log( 'deactivate', $value['module_slug'] );
668
					Jetpack::deactivate_module( $value['module_slug'] );
669
					Jetpack::state( 'message', 'no_message' );
670
				} else {
671
					Jetpack::log( 'activate', $value['module_slug'] );
672
					Jetpack::activate_module( $value['module_slug'], false, false );
673
					Jetpack::state( 'message', 'no_message' );
674
				}
675
			}
676
677
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
678
			echo "reload the page";
679
		}
680
681
		wp_die();
682
	}
683
684
	/**
685
	 * The callback for the JITM ajax requests.
686
	 */
687
	function jetpack_jitm_ajax_callback() {
688
		// Check for nonce
689
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
690
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
691
		}
692
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
693
			$module_slug = $_REQUEST['jitmModule'];
694
			Jetpack::log( 'activate', $module_slug );
695
			Jetpack::activate_module( $module_slug, false, false );
696
			Jetpack::state( 'message', 'no_message' );
697
698
			//A Jetpack module is being activated through a JITM, track it
699
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
700
			$this->do_stats( 'server_side' );
701
702
			wp_send_json_success();
703
		}
704
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
705
			// get the hide_jitm options array
706
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
707
			$module_slug = $_REQUEST['jitmModule'];
708
709
			if( ! $jetpack_hide_jitm ) {
710
				$jetpack_hide_jitm = array(
711
					$module_slug => 'hide'
712
				);
713
			} else {
714
				$jetpack_hide_jitm[$module_slug] = 'hide';
715
			}
716
717
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
718
719
			//jitm is being dismissed forever, track it
720
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
721
			$this->do_stats( 'server_side' );
722
723
			wp_send_json_success();
724
		}
725 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
726
			$module_slug = $_REQUEST['jitmModule'];
727
728
			// User went to WordPress.com, track this
729
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
730
			$this->do_stats( 'server_side' );
731
732
			wp_send_json_success();
733
		}
734 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'viewed' == $_REQUEST['jitmActionToTake'] ) {
735
			$track = $_REQUEST['jitmModule'];
736
737
			// User is viewing JITM, track it.
738
			$this->stat( 'jitm', $track . '-viewed-' . JETPACK__VERSION );
739
			$this->do_stats( 'server_side' );
740
741
			wp_send_json_success();
742
		}
743
	}
744
745
	/**
746
	 * If there are any stats that need to be pushed, but haven't been, push them now.
747
	 */
748
	function __destruct() {
749
		if ( ! empty( $this->stats ) ) {
750
			$this->do_stats( 'server_side' );
751
		}
752
	}
753
754
	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...
755
		switch( $cap ) {
756
			case 'jetpack_connect' :
757
			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...
758
				if ( Jetpack::is_development_mode() ) {
759
					$caps = array( 'do_not_allow' );
760
					break;
761
				}
762
				/**
763
				 * Pass through. If it's not development mode, these should match disconnect.
764
				 * Let users disconnect if it's development mode, just in case things glitch.
765
				 */
766
			case 'jetpack_disconnect' :
767
				/**
768
				 * In multisite, can individual site admins manage their own connection?
769
				 *
770
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
771
				 */
772
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
773
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
774
						/**
775
						 * We need to update the option name -- it's terribly unclear which
776
						 * direction the override goes.
777
						 *
778
						 * @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...
779
						 */
780
						$caps = array( 'do_not_allow' );
781
						break;
782
					}
783
				}
784
785
				$caps = array( 'manage_options' );
786
				break;
787
			case 'jetpack_manage_modules' :
788
			case 'jetpack_activate_modules' :
789
			case 'jetpack_deactivate_modules' :
790
				$caps = array( 'manage_options' );
791
				break;
792
			case 'jetpack_configure_modules' :
793
				$caps = array( 'manage_options' );
794
				break;
795
			case 'jetpack_network_admin_page':
796
			case 'jetpack_network_settings_page':
797
				$caps = array( 'manage_network_plugins' );
798
				break;
799
			case 'jetpack_network_sites_page':
800
				$caps = array( 'manage_sites' );
801
				break;
802
			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...
803
				if ( Jetpack::is_development_mode() ) {
804
					$caps = array( 'manage_options' );
805
					break;
806
				}
807
808
				// Don't ever show to subscribers, but allow access to the page if they're trying to unlink.
809
				if ( ! current_user_can( 'edit_posts' ) ) {
810
					if ( isset( $_GET['redirect'] ) && 'sub-unlink' == $_GET['redirect'] ) {
811
						// We need this in order to unlink the user.
812
						$this->admin_page_load();
813
					}
814
					if ( ! wp_verify_nonce( 'jetpack-unlink' ) ) {
815
						$caps = array( 'do_not_allow' );
816
						break;
817
					}
818
				}
819
820
				if ( ! self::is_active() && ! current_user_can( 'jetpack_connect' ) ) {
821
					$caps = array( 'do_not_allow' );
822
					break;
823
				}
824
				/**
825
				 * Pass through. If it's not development mode, these should match the admin page.
826
				 * Let users disconnect if it's development mode, just in case things glitch.
827
				 */
828
			case 'jetpack_connect_user' :
829
				if ( Jetpack::is_development_mode() ) {
830
					$caps = array( 'do_not_allow' );
831
					break;
832
				}
833
				$caps = array( 'read' );
834
				break;
835
		}
836
		return $caps;
837
	}
838
839
	function require_jetpack_authentication() {
840
		// Don't let anyone authenticate
841
		$_COOKIE = array();
842
		remove_all_filters( 'authenticate' );
843
844
		/**
845
		 * For the moment, remove Limit Login Attempts if its xmlrpc for Jetpack.
846
		 * If Limit Login Attempts is installed as a mu-plugin, it can occasionally
847
		 * generate false-positives.
848
		 */
849
		remove_filter( 'wp_login_failed', 'limit_login_failed' );
850
851
		if ( Jetpack::is_active() ) {
852
			// Allow Jetpack authentication
853
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
854
		}
855
	}
856
857
	/**
858
	 * Load language files
859
	 */
860
	public static function plugin_textdomain() {
861
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
862
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
863
	}
864
865
	/**
866
	 * Register assets for use in various modules and the Jetpack admin page.
867
	 *
868
	 * @uses wp_script_is, wp_register_script, plugins_url
869
	 * @action wp_loaded
870
	 * @return null
871
	 */
872
	public function register_assets() {
873
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
874
			wp_register_script( 'spin', plugins_url( '_inc/spin.js', JETPACK__PLUGIN_FILE ), false, '1.3' );
875
		}
876
877
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
878
			wp_register_script( 'jquery.spin', plugins_url( '_inc/jquery.spin.js', JETPACK__PLUGIN_FILE ) , array( 'jquery', 'spin' ), '1.3' );
879
		}
880
881 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
882
			wp_register_script( 'jetpack-gallery-settings', plugins_url( '_inc/gallery-settings.js', JETPACK__PLUGIN_FILE ), array( 'media-views' ), '20121225' );
883
		}
884
885
		/**
886
		 * As jetpack_register_genericons is by default fired off a hook,
887
		 * the hook may have already fired by this point.
888
		 * So, let's just trigger it manually.
889
		 */
890
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
891
		jetpack_register_genericons();
892
893 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
894
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
895
	}
896
897
	/**
898
	 * Device Pixels support
899
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
900
	 */
901
	function devicepx() {
902
		if ( Jetpack::is_active() ) {
903
			wp_enqueue_script( 'devicepx', set_url_scheme( 'http://s0.wp.com/wp-content/js/devicepx-jetpack.js' ), array(), gmdate( 'oW' ), true );
904
		}
905
	}
906
907
	/*
908
	 * Returns the location of Jetpack's lib directory. This filter is applied
909
	 * in require_lib().
910
	 *
911
	 * @filter require_lib_dir
912
	 */
913
	function require_lib_dir() {
914
		return JETPACK__PLUGIN_DIR . '_inc/lib';
915
	}
916
917
	/**
918
	 * Return the network_site_url so that .com knows what network this site is a part of.
919
	 * @param  bool $option
920
	 * @return string
921
	 */
922
	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...
923
		return network_site_url();
924
	}
925
	/**
926
	 * Network Name.
927
	 */
928
	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...
929
		global $current_site;
930
		if( is_null( $current_site ) ) {
931
			$current_site = get_current_site();
932
		}
933
		return $current_site->site_name;
934
	}
935
	/**
936
	 * Does the network allow new user and site registrations.
937
	 * @return string
938
	 */
939
	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...
940
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
941
	}
942
	/**
943
	 * Does the network allow admins to add new users.
944
	 * @return boolian
945
	 */
946
	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...
947
		return (bool) get_site_option( 'add_new_users' );
948
	}
949
	/**
950
	 * File upload psace left per site in MB.
951
	 *  -1 means NO LIMIT.
952
	 * @return number
953
	 */
954
	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...
955
		// value in MB
956
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
957
	}
958
959
	/**
960
	 * Network allowed file types.
961
	 * @return string
962
	 */
963
	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...
964
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
965
	}
966
967
	/**
968
	 * Maximum file upload size set by the network.
969
	 * @return number
970
	 */
971
	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...
972
		// value in KB
973
		return get_site_option( 'fileupload_maxk', 300 );
974
	}
975
976
	/**
977
	 * Lets us know if a site allows admins to manage the network.
978
	 * @return array
979
	 */
980
	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...
981
		return get_site_option( 'menu_items' );
982
	}
983
984
	/**
985
	 * Return whether we are dealing with a multi network setup or not.
986
	 * The reason we are type casting this is because we want to avoid the situation where
987
	 * the result is false since when is_main_network_option return false it cases
988
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
989
	 * database which could be set to anything as opposed to what this function returns.
990
	 * @param  bool  $option
991
	 *
992
	 * @return boolean
993
	 */
994
	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...
995
		// return '1' or ''
996
		return (string) (bool) Jetpack::is_multi_network();
997
	}
998
999
	/**
1000
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1001
	 *
1002
	 * @param  string  $option
1003
	 * @return boolean
1004
	 */
1005
	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...
1006
		return (string) (bool) is_multisite();
1007
	}
1008
1009
	/**
1010
	 * Implemented since there is no core is multi network function
1011
	 * Right now there is no way to tell if we which network is the dominant network on the system
1012
	 *
1013
	 * @since  3.3
1014
	 * @return boolean
1015
	 */
1016
	public static function is_multi_network() {
1017
		global  $wpdb;
1018
1019
		// if we don't have a multi site setup no need to do any more
1020
		if ( ! is_multisite() ) {
1021
			return false;
1022
		}
1023
1024
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1025
		if ( $num_sites > 1 ) {
1026
			return true;
1027
		} else {
1028
			return false;
1029
		}
1030
	}
1031
1032
	/**
1033
	 * Get back if the current site is single user site.
1034
	 *
1035
	 * @return bool
1036
	 */
1037
	public static function is_single_user_site() {
1038
1039
		$user_query = new WP_User_Query( array(
1040
			'blog_id' => get_current_blog_id(),
1041
			'fields'  => 'ID',
1042
			'number' => 2
1043
		) );
1044
		return 1 === (int) $user_query->get_total();
1045
	}
1046
1047
	/**
1048
	 * Determines whether the current theme supports featured images or not.
1049
	 * @return string ( '1' | '0' )
1050
	 */
1051
	static function featured_images_enabled() {
1052
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1053
	}
1054
1055
	/**
1056
	 * Invalides the transient as well as triggers the update of the mock option.
1057
	 *
1058
	 * @return null
1059
	 */
1060
	function is_single_user_site_invalidate() {
1061
		/**
1062
		 * Fires when a user is added or removed from a site.
1063
		 * Determines if the site is a single user site.
1064
		 *
1065
		 * @since 3.4.0
1066
		 *
1067
		 * @param string jetpack_single_user_site.
1068
		 * @param bool Jetpack::is_single_user_site() Is the current site a single user site.
1069
		 */
1070
		do_action( 'update_option_jetpack_single_user_site', 'jetpack_single_user_site', (bool) Jetpack::is_single_user_site() );
1071
	}
1072
1073
	/**
1074
	 * Is Jetpack active?
1075
	 */
1076
	public static function is_active() {
1077
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1078
	}
1079
1080
	/**
1081
	 * Is Jetpack in development (offline) mode?
1082
	 */
1083
	public static function is_development_mode() {
1084
		$development_mode = false;
1085
1086
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1087
			$development_mode = JETPACK_DEV_DEBUG;
1088
		}
1089
1090
		elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1091
			$development_mode = true;
1092
		}
1093
		/**
1094
		 * Filters Jetpack's development mode.
1095
		 *
1096
		 * @see http://jetpack.me/support/development-mode/
1097
		 *
1098
		 * @since 2.2.1
1099
		 *
1100
		 * @param bool $development_mode Is Jetpack's development mode active.
1101
		 */
1102
		return apply_filters( 'jetpack_development_mode', $development_mode );
1103
	}
1104
1105
	/**
1106
	* Get Jetpack development mode notice text and notice class.
1107
	*
1108
	* Mirrors the checks made in Jetpack::is_development_mode
1109
	*
1110
	*/
1111
	public static function show_development_mode_notice() {
1112
		if ( Jetpack::is_development_mode() ) {
1113
			if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1114
				$notice = sprintf(
1115
					/* translators: %s is a URL */
1116
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the JETPACK_DEV_DEBUG constant being defined in wp-config.php or elsewhere.', 'jetpack' ),
1117
					'http://jetpack.me/support/development-mode/'
1118
				);
1119
			} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1120
				$notice = sprintf(
1121
					/* translators: %s is a URL */
1122
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via site URL lacking a dot (e.g. http://localhost).', 'jetpack' ),
1123
					'http://jetpack.me/support/development-mode/'
1124
				);
1125
			} else {
1126
				$notice = sprintf(
1127
					/* translators: %s is a URL */
1128
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the jetpack_development_mode filter.', 'jetpack' ),
1129
					'http://jetpack.me/support/development-mode/'
1130
				);
1131
			}
1132
1133
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1134
		}
1135
1136
		// Throw up a notice if using a development version and as for feedback.
1137
		if ( Jetpack::is_development_version() ) {
1138
			/* translators: %s is a URL */
1139
			$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/' );
1140
1141
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1142
		}
1143
		// Throw up a notice if using staging mode
1144
		if ( Jetpack::is_staging_site() ) {
1145
			/* translators: %s is a URL */
1146
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.me/support/staging-sites/' );
1147
1148
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1149
		}
1150
	}
1151
1152
	/**
1153
	 * Whether Jetpack's version maps to a public release, or a development version.
1154
	 */
1155
	public static function is_development_version() {
1156
		return ! preg_match( '/^\d+(\.\d+)+$/', JETPACK__VERSION );
1157
	}
1158
1159
	/**
1160
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1161
	 */
1162
	public static function is_user_connected( $user_id = false ) {
1163
		$user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
1164
		if ( ! $user_id ) {
1165
			return false;
1166
		}
1167
		return (bool) Jetpack_Data::get_access_token( $user_id );
1168
	}
1169
1170
	/**
1171
	 * Get the wpcom user data of the current|specified connected user.
1172
	 */
1173 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1174
		if ( ! $user_id ) {
1175
			$user_id = get_current_user_id();
1176
		}
1177
		Jetpack::load_xml_rpc_client();
1178
		$xml = new Jetpack_IXR_Client( array(
1179
			'user_id' => $user_id,
1180
		) );
1181
		$xml->query( 'wpcom.getUser' );
1182
		if ( ! $xml->isError() ) {
1183
			return $xml->getResponse();
1184
		}
1185
		return false;
1186
	}
1187
1188
	/**
1189
	 * Get the wpcom email of the current|specified connected user.
1190
	 */
1191 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1192
		if ( ! $user_id ) {
1193
			$user_id = get_current_user_id();
1194
		}
1195
		Jetpack::load_xml_rpc_client();
1196
		$xml = new Jetpack_IXR_Client( array(
1197
			'user_id' => $user_id,
1198
		) );
1199
		$xml->query( 'wpcom.getUserEmail' );
1200
		if ( ! $xml->isError() ) {
1201
			return $xml->getResponse();
1202
		}
1203
		return false;
1204
	}
1205
1206
	/**
1207
	 * Get the wpcom email of the master user.
1208
	 */
1209
	public static function get_master_user_email() {
1210
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1211
		if ( $master_user_id ) {
1212
			return self::get_connected_user_email( $master_user_id );
1213
		}
1214
		return '';
1215
	}
1216
1217
	static function current_user_is_connection_owner() {
1218
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1219
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1220
	}
1221
1222
	/**
1223
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1224
	 */
1225
	function extra_oembed_providers() {
1226
		// Cloudup: https://dev.cloudup.com/#oembed
1227
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1228
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1229
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1230
		wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true );
1231
		wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true );
1232
	}
1233
1234
	/**
1235
	 * Loads the currently active modules.
1236
	 */
1237
	public static function load_modules() {
1238
		if ( ! self::is_active() && !self::is_development_mode() ) {
1239
			if ( ! is_multisite() || ! get_site_option( 'jetpack_protect_active' ) ) {
1240
				return;
1241
			}
1242
		}
1243
1244
		$version = Jetpack_Options::get_option( 'version' );
1245 View Code Duplication
		if ( ! $version ) {
1246
			$version = $old_version = JETPACK__VERSION . ':' . time();
1247
			/** This action is documented in class.jetpack.php */
1248
			do_action( 'updating_jetpack_version', $version, false );
1249
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1250
		}
1251
		list( $version ) = explode( ':', $version );
1252
1253
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1254
1255
		$modules_data = array();
1256
1257
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1258
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1259
			$updated_modules = array();
1260
			foreach ( $modules as $module ) {
1261
				$modules_data[ $module ] = Jetpack::get_module( $module );
1262
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1263
					continue;
1264
				}
1265
1266
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1267
					continue;
1268
				}
1269
1270
				$updated_modules[] = $module;
1271
			}
1272
1273
			$modules = array_diff( $modules, $updated_modules );
1274
		}
1275
1276
		$is_development_mode = Jetpack::is_development_mode();
1277
1278
		foreach ( $modules as $index => $module ) {
1279
			// If we're in dev mode, disable modules requiring a connection
1280
			if ( $is_development_mode ) {
1281
				// Prime the pump if we need to
1282
				if ( empty( $modules_data[ $module ] ) ) {
1283
					$modules_data[ $module ] = Jetpack::get_module( $module );
1284
				}
1285
				// If the module requires a connection, but we're in local mode, don't include it.
1286
				if ( $modules_data[ $module ]['requires_connection'] ) {
1287
					continue;
1288
				}
1289
			}
1290
1291
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1292
				continue;
1293
			}
1294
1295
			if ( ! @include( Jetpack::get_module_path( $module ) ) ) {
1296
				unset( $modules[ $index ] );
1297
				Jetpack_Options::update_option( 'active_modules', array_values( $modules ) );
1298
				continue;
1299
			}
1300
1301
			/**
1302
			 * Fires when a specific module is loaded.
1303
			 * The dynamic part of the hook, $module, is the module slug.
1304
			 *
1305
			 * @since 1.1.0
1306
			 */
1307
			do_action( 'jetpack_module_loaded_' . $module );
1308
		}
1309
1310
		/**
1311
		 * Fires when all the modules are loaded.
1312
		 *
1313
		 * @since 1.1.0
1314
		 */
1315
		do_action( 'jetpack_modules_loaded' );
1316
1317
		// 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.
1318
		if ( Jetpack::is_active() || Jetpack::is_development_mode() )
1319
			require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
1320
	}
1321
1322
	/**
1323
	 * Check if Jetpack's REST API compat file should be included
1324
	 * @action plugins_loaded
1325
	 * @return null
1326
	 */
1327
	public function check_rest_api_compat() {
1328
		/**
1329
		 * Filters the list of REST API compat files to be included.
1330
		 *
1331
		 * @since 2.2.5
1332
		 *
1333
		 * @param array $args Array of REST API compat files to include.
1334
		 */
1335
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1336
1337
		if ( function_exists( 'bbpress' ) )
1338
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
1339
1340
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
1341
			require_once $_jetpack_rest_api_compat_include;
1342
	}
1343
1344
	/**
1345
	 * Gets all plugins currently active in values, regardless of whether they're
1346
	 * traditionally activated or network activated.
1347
	 *
1348
	 * @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...
1349
	 */
1350
	public static function get_active_plugins() {
1351
		$active_plugins = (array) get_option( 'active_plugins', array() );
1352
1353
		if ( is_multisite() ) {
1354
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1355
			// whereas active_plugins stores them in the values.
1356
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1357
			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...
1358
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1359
			}
1360
		}
1361
1362
		sort( $active_plugins );
1363
1364
		return array_unique( $active_plugins );
1365
	}
1366
1367
	/**
1368
	 * Gets and parses additional plugin data to send with the heartbeat data
1369
	 *
1370
	 * @since 3.8.1
1371
	 *
1372
	 * @return array Array of plugin data
1373
	 */
1374
	public static function get_parsed_plugin_data() {
1375
		if ( ! function_exists( 'get_plugins' ) ) {
1376
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1377
		}
1378
		$all_plugins    = get_plugins();
1379
		$active_plugins = Jetpack::get_active_plugins();
1380
1381
		$plugins = array();
1382
		foreach ( $all_plugins as $path => $plugin_data ) {
1383
			$plugins[ $path ] = array(
1384
					'is_active' => in_array( $path, $active_plugins ),
1385
					'file'      => $path,
1386
					'name'      => $plugin_data['Name'],
1387
					'version'   => $plugin_data['Version'],
1388
					'author'    => $plugin_data['Author'],
1389
			);
1390
		}
1391
1392
		return $plugins;
1393
	}
1394
1395
	/**
1396
	 * Gets and parses theme data to send with the heartbeat data
1397
	 *
1398
	 * @since 3.8.1
1399
	 *
1400
	 * @return array Array of theme data
1401
	 */
1402
	public static function get_parsed_theme_data() {
1403
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
1404
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
1405
1406
		$themes = array();
1407
		foreach ( $all_themes as $slug => $theme_data ) {
1408
			$theme_headers = array();
1409
			foreach ( $header_keys as $header_key ) {
1410
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
1411
			}
1412
1413
			$themes[ $slug ] = array(
1414
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
1415
					'slug' => $slug,
1416
					'theme_root' => $theme_data->get_theme_root_uri(),
1417
					'parent' => $theme_data->parent(),
1418
					'headers' => $theme_headers
1419
			);
1420
		}
1421
1422
		return $themes;
1423
	}
1424
1425
	/**
1426
	 * Checks whether a specific plugin is active.
1427
	 *
1428
	 * We don't want to store these in a static variable, in case
1429
	 * there are switch_to_blog() calls involved.
1430
	 */
1431
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
1432
		return in_array( $plugin, self::get_active_plugins() );
1433
	}
1434
1435
	/**
1436
	 * Check if Jetpack's Open Graph tags should be used.
1437
	 * If certain plugins are active, Jetpack's og tags are suppressed.
1438
	 *
1439
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1440
	 * @action plugins_loaded
1441
	 * @return null
1442
	 */
1443
	public function check_open_graph() {
1444
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
1445
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
1446
		}
1447
1448
		$active_plugins = self::get_active_plugins();
1449
1450
		if ( ! empty( $active_plugins ) ) {
1451
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
1452
				if ( in_array( $plugin, $active_plugins ) ) {
1453
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
1454
					break;
1455
				}
1456
			}
1457
		}
1458
1459
		/**
1460
		 * Allow the addition of Open Graph Meta Tags to all pages.
1461
		 *
1462
		 * @since 2.0.3
1463
		 *
1464
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
1465
		 */
1466
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
1467
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
1468
		}
1469
	}
1470
1471
	/**
1472
	 * Check if Jetpack's Twitter tags should be used.
1473
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
1474
	 *
1475
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1476
	 * @action plugins_loaded
1477
	 * @return null
1478
	 */
1479
	public function check_twitter_tags() {
1480
1481
		$active_plugins = self::get_active_plugins();
1482
1483
		if ( ! empty( $active_plugins ) ) {
1484
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
1485
				if ( in_array( $plugin, $active_plugins ) ) {
1486
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
1487
					break;
1488
				}
1489
			}
1490
		}
1491
1492
		/**
1493
		 * Allow Twitter Card Meta tags to be disabled.
1494
		 *
1495
		 * @since 2.6.0
1496
		 *
1497
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
1498
		 */
1499
		if ( apply_filters( 'jetpack_disable_twitter_cards', true ) ) {
1500
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
1501
		}
1502
	}
1503
1504
1505
1506
1507
	/*
1508
	 *
1509
	 * Jetpack Security Reports
1510
	 *
1511
	 * Allowed types: login_form, backup, file_scanning, spam
1512
	 *
1513
	 * 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)
1514
	 *
1515
	 * 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)
1516
	 *
1517
	 *
1518
	 * Example code to submit a security report:
1519
	 *
1520
	 *  function akismet_submit_jetpack_security_report() {
1521
	 *  	Jetpack::submit_security_report( 'spam', __FILE__, $args = array( 'blocked' => 138284, status => 'ok' ) );
1522
	 *  }
1523
	 *  add_action( 'jetpack_security_report', 'akismet_submit_jetpack_security_report' );
1524
	 *
1525
	 */
1526
1527
1528
	/**
1529
	 * Calls for security report submissions.
1530
	 *
1531
	 * @return null
1532
	 */
1533
	public static function perform_security_reporting() {
1534
		$no_check_needed = get_site_transient( 'security_report_performed_recently' );
1535
1536
		if ( $no_check_needed ) {
1537
			return;
1538
		}
1539
1540
		/**
1541
		 * Fires before a security report is created.
1542
		 *
1543
		 * @since 3.4.0
1544
		 */
1545
		do_action( 'jetpack_security_report' );
1546
1547
		Jetpack_Options::update_option( 'security_report', self::$security_report );
1548
		set_site_transient( 'security_report_performed_recently', 1, 15 * MINUTE_IN_SECONDS );
1549
	}
1550
1551
	/**
1552
	 * Allows plugins to submit security reports.
1553
 	 *
1554
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
1555
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
1556
	 * @param array   $args         See definitions above
1557
	 */
1558
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
1559
1560
		if( !doing_action( 'jetpack_security_report' ) ) {
1561
			return new WP_Error( 'not_collecting_report', 'Not currently collecting security reports.  Please use the jetpack_security_report hook.' );
1562
		}
1563
1564
		if( !is_string( $type ) || !is_string( $plugin_file ) ) {
1565
			return new WP_Error( 'invalid_security_report', 'Invalid Security Report' );
1566
		}
1567
1568
		if( !function_exists( 'get_plugin_data' ) ) {
1569
			include( ABSPATH . 'wp-admin/includes/plugin.php' );
1570
		}
1571
1572
		//Get rid of any non-allowed args
1573
		$args = array_intersect_key( $args, array_flip( array( 'blocked', 'last', 'next', 'status', 'message' ) ) );
1574
1575
		$plugin = get_plugin_data( $plugin_file );
1576
1577
		if ( !$plugin['Name'] ) {
1578
			return new WP_Error( 'security_report_missing_plugin_name', 'Invalid Plugin File Provided' );
1579
		}
1580
1581
		// Sanitize everything to make sure we're not syncing something wonky
1582
		$type = sanitize_key( $type );
1583
1584
		$args['plugin'] = $plugin;
1585
1586
		// Cast blocked, last and next as integers.
1587
		// Last and next should be in unix timestamp format
1588
		if ( isset( $args['blocked'] ) ) {
1589
			$args['blocked'] = (int) $args['blocked'];
1590
		}
1591
		if ( isset( $args['last'] ) ) {
1592
			$args['last'] = (int) $args['last'];
1593
		}
1594
		if ( isset( $args['next'] ) ) {
1595
			$args['next'] = (int) $args['next'];
1596
		}
1597
		if ( !in_array( $args['status'], array( 'ok', 'warning', 'error' ) ) ) {
1598
			$args['status'] = 'ok';
1599
		}
1600
		if ( isset( $args['message'] ) ) {
1601
1602
			if( $args['status'] == 'ok' ) {
1603
				unset( $args['message'] );
1604
			}
1605
1606
			$allowed_html = array(
1607
			    'a' => array(
1608
			        'href' => array(),
1609
			        'title' => array()
1610
			    ),
1611
			    'em' => array(),
1612
			    'strong' => array(),
1613
			);
1614
1615
			$args['message'] = wp_kses( $args['message'], $allowed_html );
1616
		}
1617
1618
		$plugin_name = $plugin[ 'Name' ];
1619
1620
		self::$security_report[ $type ][ $plugin_name ] = $args;
1621
	}
1622
1623
	/**
1624
	 * Collects a new report if needed, then returns it.
1625
	 */
1626
	public function get_security_report() {
1627
		self::perform_security_reporting();
1628
		return Jetpack_Options::get_option( 'security_report' );
1629
	}
1630
1631
1632
/* Jetpack Options API */
1633
1634
	public static function get_option_names( $type = 'compact' ) {
1635
		return Jetpack_Options::get_option_names( $type );
1636
	}
1637
1638
	/**
1639
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
1640
 	 *
1641
	 * @param string $name    Option name
1642
	 * @param mixed  $default (optional)
1643
	 */
1644
	public static function get_option( $name, $default = false ) {
1645
		return Jetpack_Options::get_option( $name, $default );
1646
	}
1647
1648
	/**
1649
	* Stores two secrets and a timestamp so WordPress.com can make a request back and verify an action
1650
	* Does some extra verification so urls (such as those to public-api, register, etc) can't just be crafted
1651
	* $name must be a registered option name.
1652
	*/
1653
	public static function create_nonce( $name ) {
1654
		$secret = wp_generate_password( 32, false ) . ':' . wp_generate_password( 32, false ) . ':' . ( time() + 600 );
1655
1656
		Jetpack_Options::update_option( $name, $secret );
1657
		@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...
1658
		if ( empty( $secret_1 ) || empty( $secret_2 ) || $eol < time() )
1659
			return new Jetpack_Error( 'missing_secrets' );
1660
1661
		return array(
1662
			'secret_1' => $secret_1,
1663
			'secret_2' => $secret_2,
1664
			'eol'      => $eol,
1665
		);
1666
	}
1667
1668
	/**
1669
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
1670
 	 *
1671
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
1672
	 * @param string $name  Option name
1673
	 * @param mixed  $value Option value
1674
	 */
1675
	public static function update_option( $name, $value ) {
1676
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
1677
		return Jetpack_Options::update_option( $name, $value );
1678
	}
1679
1680
	/**
1681
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
1682
 	 *
1683
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
1684
	 * @param array $array array( option name => option value, ... )
1685
	 */
1686
	public static function update_options( $array ) {
1687
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
1688
		return Jetpack_Options::update_options( $array );
1689
	}
1690
1691
	/**
1692
	 * Deletes the given option.  May be passed multiple option names as an array.
1693
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
1694
	 *
1695
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
1696
	 * @param string|array $names
1697
	 */
1698
	public static function delete_option( $names ) {
1699
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
1700
		return Jetpack_Options::delete_option( $names );
1701
	}
1702
1703
	/**
1704
	 * Enters a user token into the user_tokens option
1705
	 *
1706
	 * @param int $user_id
1707
	 * @param string $token
1708
	 * return bool
1709
	 */
1710
	public static function update_user_token( $user_id, $token, $is_master_user ) {
1711
		// not designed for concurrent updates
1712
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
1713
		if ( ! is_array( $user_tokens ) )
1714
			$user_tokens = array();
1715
		$user_tokens[$user_id] = $token;
1716
		if ( $is_master_user ) {
1717
			$master_user = $user_id;
1718
			$options     = compact( 'user_tokens', 'master_user' );
1719
		} else {
1720
			$options = compact( 'user_tokens' );
1721
		}
1722
		return Jetpack_Options::update_options( $options );
1723
	}
1724
1725
	/**
1726
	 * Returns an array of all PHP files in the specified absolute path.
1727
	 * Equivalent to glob( "$absolute_path/*.php" ).
1728
	 *
1729
	 * @param string $absolute_path The absolute path of the directory to search.
1730
	 * @return array Array of absolute paths to the PHP files.
1731
	 */
1732
	public static function glob_php( $absolute_path ) {
1733
		if ( function_exists( 'glob' ) ) {
1734
			return glob( "$absolute_path/*.php" );
1735
		}
1736
1737
		$absolute_path = untrailingslashit( $absolute_path );
1738
		$files = array();
1739
		if ( ! $dir = @opendir( $absolute_path ) ) {
1740
			return $files;
1741
		}
1742
1743
		while ( false !== $file = readdir( $dir ) ) {
1744
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
1745
				continue;
1746
			}
1747
1748
			$file = "$absolute_path/$file";
1749
1750
			if ( ! is_file( $file ) ) {
1751
				continue;
1752
			}
1753
1754
			$files[] = $file;
1755
		}
1756
1757
		closedir( $dir );
1758
1759
		return $files;
1760
	}
1761
1762
	public static function activate_new_modules( $redirect = false ) {
1763
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
1764
			return;
1765
		}
1766
1767
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
1768 View Code Duplication
		if ( ! $jetpack_old_version ) {
1769
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
1770
			/** This action is documented in class.jetpack.php */
1771
			do_action( 'updating_jetpack_version', $version, false );
1772
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1773
		}
1774
1775
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
1776
1777
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
1778
			return;
1779
		}
1780
1781
		$active_modules     = Jetpack::get_active_modules();
1782
		$reactivate_modules = array();
1783
		foreach ( $active_modules as $active_module ) {
1784
			$module = Jetpack::get_module( $active_module );
1785
			if ( ! isset( $module['changed'] ) ) {
1786
				continue;
1787
			}
1788
1789
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
1790
				continue;
1791
			}
1792
1793
			$reactivate_modules[] = $active_module;
1794
			Jetpack::deactivate_module( $active_module );
1795
		}
1796
1797
		$new_version = JETPACK__VERSION . ':' . time();
1798
		/** This action is documented in class.jetpack.php */
1799
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
1800
		Jetpack_Options::update_options(
1801
			array(
1802
				'version'     => $new_version,
1803
				'old_version' => $jetpack_old_version,
1804
			)
1805
		);
1806
1807
		Jetpack::state( 'message', 'modules_activated' );
1808
		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...
1809
1810
		if ( $redirect ) {
1811
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
1812
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
1813
				$page = $_GET['page'];
1814
			}
1815
1816
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
1817
			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...
1818
		}
1819
	}
1820
1821
	/**
1822
	 * List available Jetpack modules. Simply lists .php files in /modules/.
1823
	 * Make sure to tuck away module "library" files in a sub-directory.
1824
	 */
1825
	public static function get_available_modules( $min_version = false, $max_version = false ) {
1826
		static $modules = null;
1827
1828
		if ( ! isset( $modules ) ) {
1829
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
1830
			// Use the cache if we're on the front-end and it's available...
1831
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
1832
				$modules = $available_modules_option[ JETPACK__VERSION ];
1833
			} else {
1834
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
1835
1836
				$modules = array();
1837
1838
				foreach ( $files as $file ) {
1839
					if ( ! $headers = Jetpack::get_module( $file ) ) {
1840
						continue;
1841
					}
1842
1843
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
1844
				}
1845
1846
				Jetpack_Options::update_option( 'available_modules', array(
1847
					JETPACK__VERSION => $modules,
1848
				) );
1849
			}
1850
		}
1851
1852
		/**
1853
		 * Filters the array of modules available to be activated.
1854
		 *
1855
		 * @since 2.4.0
1856
		 *
1857
		 * @param array $modules Array of available modules.
1858
		 * @param string $min_version Minimum version number required to use modules.
1859
		 * @param string $max_version Maximum version number required to use modules.
1860
		 */
1861
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
1862
1863
		if ( ! $min_version && ! $max_version ) {
1864
			return array_keys( $mods );
1865
		}
1866
1867
		$r = array();
1868
		foreach ( $mods as $slug => $introduced ) {
1869
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
1870
				continue;
1871
			}
1872
1873
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
1874
				continue;
1875
			}
1876
1877
			$r[] = $slug;
1878
		}
1879
1880
		return $r;
1881
	}
1882
1883
	/**
1884
	 * Default modules loaded on activation.
1885
	 */
1886
	public static function get_default_modules( $min_version = false, $max_version = false ) {
1887
		$return = array();
1888
1889
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
1890
			$module_data = Jetpack::get_module( $module );
1891
1892
			switch ( strtolower( $module_data['auto_activate'] ) ) {
1893
				case 'yes' :
1894
					$return[] = $module;
1895
					break;
1896
				case 'public' :
1897
					if ( Jetpack_Options::get_option( 'public' ) ) {
1898
						$return[] = $module;
1899
					}
1900
					break;
1901
				case 'no' :
1902
				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...
1903
					break;
1904
			}
1905
		}
1906
		/**
1907
		 * Filters the array of default modules.
1908
		 *
1909
		 * @since 2.5.0
1910
		 *
1911
		 * @param array $return Array of default modules.
1912
		 * @param string $min_version Minimum version number required to use modules.
1913
		 * @param string $max_version Maximum version number required to use modules.
1914
		 */
1915
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
1916
	}
1917
1918
	/**
1919
	 * Checks activated modules during auto-activation to determine
1920
	 * if any of those modules are being deprecated.  If so, close
1921
	 * them out, and add any replacement modules.
1922
	 *
1923
	 * Runs at priority 99 by default.
1924
	 *
1925
	 * This is run late, so that it can still activate a module if
1926
	 * the new module is a replacement for another that the user
1927
	 * currently has active, even if something at the normal priority
1928
	 * would kibosh everything.
1929
	 *
1930
	 * @since 2.6
1931
	 * @uses jetpack_get_default_modules filter
1932
	 * @param array $modules
1933
	 * @return array
1934
	 */
1935
	function handle_deprecated_modules( $modules ) {
1936
		$deprecated_modules = array(
1937
			'debug'            => null,  // Closed out and moved to ./class.jetpack-debugger.php
1938
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
1939
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
1940
		);
1941
1942
		// Don't activate SSO if they never completed activating WPCC.
1943
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
1944
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
1945
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
1946
				$deprecated_modules['wpcc'] = null;
1947
			}
1948
		}
1949
1950
		foreach ( $deprecated_modules as $module => $replacement ) {
1951
			if ( Jetpack::is_module_active( $module ) ) {
1952
				self::deactivate_module( $module );
1953
				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...
1954
					$modules[] = $replacement;
1955
				}
1956
			}
1957
		}
1958
1959
		return array_unique( $modules );
1960
	}
1961
1962
	/**
1963
	 * Checks activated plugins during auto-activation to determine
1964
	 * if any of those plugins are in the list with a corresponding module
1965
	 * that is not compatible with the plugin. The module will not be allowed
1966
	 * to auto-activate.
1967
	 *
1968
	 * @since 2.6
1969
	 * @uses jetpack_get_default_modules filter
1970
	 * @param array $modules
1971
	 * @return array
1972
	 */
1973
	function filter_default_modules( $modules ) {
1974
1975
		$active_plugins = self::get_active_plugins();
1976
1977
		if ( ! empty( $active_plugins ) ) {
1978
1979
			// For each module we'd like to auto-activate...
1980
			foreach ( $modules as $key => $module ) {
1981
				// If there are potential conflicts for it...
1982
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
1983
					// For each potential conflict...
1984
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
1985
						// If that conflicting plugin is active...
1986
						if ( in_array( $plugin, $active_plugins ) ) {
1987
							// Remove that item from being auto-activated.
1988
							unset( $modules[ $key ] );
1989
						}
1990
					}
1991
				}
1992
			}
1993
		}
1994
1995
		return $modules;
1996
	}
1997
1998
	/**
1999
	 * Extract a module's slug from its full path.
2000
	 */
2001
	public static function get_module_slug( $file ) {
2002
		return str_replace( '.php', '', basename( $file ) );
2003
	}
2004
2005
	/**
2006
	 * Generate a module's path from its slug.
2007
	 */
2008
	public static function get_module_path( $slug ) {
2009
		return JETPACK__PLUGIN_DIR . "modules/$slug.php";
2010
	}
2011
2012
	/**
2013
	 * Load module data from module file. Headers differ from WordPress
2014
	 * plugin headers to avoid them being identified as standalone
2015
	 * plugins on the WordPress plugins page.
2016
	 */
2017
	public static function get_module( $module ) {
2018
		$headers = array(
2019
			'name'                      => 'Module Name',
2020
			'description'               => 'Module Description',
2021
			'jumpstart_desc'            => 'Jumpstart Description',
2022
			'sort'                      => 'Sort Order',
2023
			'recommendation_order'      => 'Recommendation Order',
2024
			'introduced'                => 'First Introduced',
2025
			'changed'                   => 'Major Changes In',
2026
			'deactivate'                => 'Deactivate',
2027
			'free'                      => 'Free',
2028
			'requires_connection'       => 'Requires Connection',
2029
			'auto_activate'             => 'Auto Activate',
2030
			'module_tags'               => 'Module Tags',
2031
			'feature'                   => 'Feature',
2032
			'additional_search_queries' => 'Additional Search Queries',
2033
		);
2034
2035
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2036
2037
		$mod = Jetpack::get_file_data( $file, $headers );
2038
		if ( empty( $mod['name'] ) ) {
2039
			return false;
2040
		}
2041
2042
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2043
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2044
		$mod['deactivate']              = empty( $mod['deactivate'] );
2045
		$mod['free']                    = empty( $mod['free'] );
2046
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2047
2048
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2049
			$mod['auto_activate'] = 'No';
2050
		} else {
2051
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2052
		}
2053
2054
		if ( $mod['module_tags'] ) {
2055
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2056
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2057
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2058
		} else {
2059
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2060
		}
2061
2062
		if ( $mod['feature'] ) {
2063
			$mod['feature'] = explode( ',', $mod['feature'] );
2064
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2065
		} else {
2066
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2067
		}
2068
2069
		/**
2070
		 * Filters the feature array on a module.
2071
		 *
2072
		 * This filter allows you to control where each module is filtered: Recommended,
2073
		 * Jumpstart, and the default "Other" listing.
2074
		 *
2075
		 * @since 3.5.0
2076
		 *
2077
		 * @param array   $mod['feature'] The areas to feature this module:
2078
		 *     'Jumpstart' adds to the "Jumpstart" option to activate many modules at once.
2079
		 *     'Recommended' shows on the main Jetpack admin screen.
2080
		 *     'Other' should be the default if no other value is in the array.
2081
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2082
		 * @param array   $mod All the currently assembled module data.
2083
		 */
2084
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2085
2086
		/**
2087
		 * Filter the returned data about a module.
2088
		 *
2089
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2090
		 * so please be careful.
2091
		 *
2092
		 * @since 3.6.0
2093
		 *
2094
		 * @param array   $mod    The details of the requested module.
2095
		 * @param string  $module The slug of the module, e.g. sharedaddy
2096
		 * @param string  $file   The path to the module source file.
2097
		 */
2098
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2099
	}
2100
2101
	/**
2102
	 * Like core's get_file_data implementation, but caches the result.
2103
	 */
2104
	public static function get_file_data( $file, $headers ) {
2105
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2106
		$file_name = basename( $file );
2107
		$file_data_option = Jetpack_Options::get_option( 'file_data', array() );
2108
		$key              = md5( $file_name . serialize( $headers ) );
2109
		$refresh_cache    = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2110
2111
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2112
		if ( ! $refresh_cache && isset( $file_data_option[ JETPACK__VERSION ][ $key ] ) ) {
2113
			return $file_data_option[ JETPACK__VERSION ][ $key ];
2114
		}
2115
2116
		$data = get_file_data( $file, $headers );
2117
2118
		// Strip out any old Jetpack versions that are cluttering the option.
2119
		$file_data_option = array_intersect_key( (array) $file_data_option, array( JETPACK__VERSION => null ) );
2120
		$file_data_option[ JETPACK__VERSION ][ $key ] = $data;
2121
		Jetpack_Options::update_option( 'file_data', $file_data_option );
2122
2123
		return $data;
2124
	}
2125
2126
	/**
2127
	 * Return translated module tag.
2128
	 *
2129
	 * @param string $tag Tag as it appears in each module heading.
2130
	 *
2131
	 * @return mixed
2132
	 */
2133
	public static function translate_module_tag( $tag ) {
2134
		return jetpack_get_module_i18n_tag( $tag );
2135
	}
2136
2137
	/**
2138
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2139
	 *
2140
	 * @since 3.9.2
2141
	 *
2142
	 * @param array $modules
2143
	 *
2144
	 * @return string|void
2145
	 */
2146
	public static function get_translated_modules( $modules ) {
2147
		foreach ( $modules as $index => $module ) {
2148
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2149
			if ( isset( $module['name'] ) ) {
2150
				$modules[ $index ]['name'] = $i18n_module['name'];
2151
			}
2152
			if ( isset( $module['description'] ) ) {
2153
				$modules[ $index ]['description'] = $i18n_module['description'];
2154
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2155
			}
2156
		}
2157
		return $modules;
2158
	}
2159
2160
	/**
2161
	 * Get a list of activated modules as an array of module slugs.
2162
	 */
2163
	public static function get_active_modules() {
2164
		$active = Jetpack_Options::get_option( 'active_modules' );
2165
		if ( ! is_array( $active ) )
2166
			$active = array();
2167
		if ( is_admin() && ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) ) {
2168
			$active[] = 'vaultpress';
2169
		} else {
2170
			$active = array_diff( $active, array( 'vaultpress' ) );
2171
		}
2172
2173
		//If protect is active on the main site of a multisite, it should be active on all sites.
2174
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2175
			$active[] = 'protect';
2176
		}
2177
2178
		return array_unique( $active );
2179
	}
2180
2181
	/**
2182
	 * Check whether or not a Jetpack module is active.
2183
	 *
2184
	 * @param string $module The slug of a Jetpack module.
2185
	 * @return bool
2186
	 *
2187
	 * @static
2188
	 */
2189
	public static function is_module_active( $module ) {
2190
		return in_array( $module, self::get_active_modules() );
2191
	}
2192
2193
	public static function is_module( $module ) {
2194
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2195
	}
2196
2197
	/**
2198
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2199
	 *
2200
	 * @param bool $catch True to start catching, False to stop.
2201
	 *
2202
	 * @static
2203
	 */
2204
	public static function catch_errors( $catch ) {
2205
		static $display_errors, $error_reporting;
2206
2207
		if ( $catch ) {
2208
			$display_errors  = @ini_set( 'display_errors', 1 );
2209
			$error_reporting = @error_reporting( E_ALL );
2210
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2211
		} else {
2212
			@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...
2213
			@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...
2214
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2215
		}
2216
	}
2217
2218
	/**
2219
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2220
	 */
2221
	public static function catch_errors_on_shutdown() {
2222
		Jetpack::state( 'php_errors', ob_get_clean() );
2223
	}
2224
2225
	public static function activate_default_modules( $min_version = false, $max_version = false, $other_modules = array() ) {
2226
		$jetpack = Jetpack::init();
2227
2228
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2229
		$modules = array_merge( $other_modules, $modules );
2230
2231
		// Look for standalone plugins and disable if active.
2232
2233
		$to_deactivate = array();
2234
		foreach ( $modules as $module ) {
2235
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2236
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2237
			}
2238
		}
2239
2240
		$deactivated = array();
2241
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2242
			list( $probable_file, $probable_title ) = $deactivate_me;
2243
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2244
				$deactivated[] = $module;
2245
			}
2246
		}
2247
2248
		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...
2249
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2250
2251
			$url = add_query_arg(
2252
				array(
2253
					'action'   => 'activate_default_modules',
2254
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2255
				),
2256
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2257
			);
2258
			wp_safe_redirect( $url );
2259
			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...
2260
		}
2261
2262
		/**
2263
		 * Fires before default modules are activated.
2264
		 *
2265
		 * @since 1.9.0
2266
		 *
2267
		 * @param string $min_version Minimum version number required to use modules.
2268
		 * @param string $max_version Maximum version number required to use modules.
2269
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2270
		 */
2271
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2272
2273
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2274
		Jetpack::restate();
2275
		Jetpack::catch_errors( true );
2276
2277
		$active = Jetpack::get_active_modules();
2278
2279
		foreach ( $modules as $module ) {
2280
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2281
				$active[] = $module;
2282
				Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2283
				continue;
2284
			}
2285
2286
			if ( in_array( $module, $active ) ) {
2287
				$module_info = Jetpack::get_module( $module );
2288
				if ( ! $module_info['deactivate'] ) {
2289
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2290 View Code Duplication
					if ( $active_state = Jetpack::state( $state ) ) {
2291
						$active_state = explode( ',', $active_state );
2292
					} else {
2293
						$active_state = array();
2294
					}
2295
					$active_state[] = $module;
2296
					Jetpack::state( $state, implode( ',', $active_state ) );
2297
				}
2298
				continue;
2299
			}
2300
2301
			$file = Jetpack::get_module_path( $module );
2302
			if ( ! file_exists( $file ) ) {
2303
				continue;
2304
			}
2305
2306
			// we'll override this later if the plugin can be included without fatal error
2307
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2308
			Jetpack::state( 'error', 'module_activation_failed' );
2309
			Jetpack::state( 'module', $module );
2310
			ob_start();
2311
			require $file;
2312
			/**
2313
			 * Fires when a specific module is activated.
2314
			 *
2315
			 * @since 1.9.0
2316
			 *
2317
			 * @param string $module Module slug.
2318
			 */
2319
			do_action( 'jetpack_activate_module', $module );
2320
			$active[] = $module;
2321
			$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2322 View Code Duplication
			if ( $active_state = Jetpack::state( $state ) ) {
2323
				$active_state = explode( ',', $active_state );
2324
			} else {
2325
				$active_state = array();
2326
			}
2327
			$active_state[] = $module;
2328
			Jetpack::state( $state, implode( ',', $active_state ) );
2329
			Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2330
			ob_end_clean();
2331
		}
2332
		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...
2333
		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...
2334
		Jetpack::catch_errors( false );
2335
		/**
2336
		 * Fires when default modules are activated.
2337
		 *
2338
		 * @since 1.9.0
2339
		 *
2340
		 * @param string $min_version Minimum version number required to use modules.
2341
		 * @param string $max_version Maximum version number required to use modules.
2342
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2343
		 */
2344
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2345
	}
2346
2347
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2348
		/**
2349
		 * Fires before a module is activated.
2350
		 *
2351
		 * @since 2.6.0
2352
		 *
2353
		 * @param string $module Module slug.
2354
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2355
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2356
		 */
2357
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2358
2359
		$jetpack = Jetpack::init();
2360
2361
		if ( ! strlen( $module ) )
2362
			return false;
2363
2364
		if ( ! Jetpack::is_module( $module ) )
2365
			return false;
2366
2367
		// If it's already active, then don't do it again
2368
		$active = Jetpack::get_active_modules();
2369
		foreach ( $active as $act ) {
2370
			if ( $act == $module )
2371
				return true;
2372
		}
2373
2374
		$module_data = Jetpack::get_module( $module );
2375
2376
		if ( ! Jetpack::is_active() ) {
2377
			if ( !Jetpack::is_development_mode() )
2378
				return false;
2379
2380
			// If we're not connected but in development mode, make sure the module doesn't require a connection
2381
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
2382
				return false;
2383
		}
2384
2385
		// Check and see if the old plugin is active
2386
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2387
			// Deactivate the old plugin
2388
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
2389
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
2390
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
2391
				Jetpack::state( 'deactivated_plugins', $module );
2392
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
2393
				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...
2394
			}
2395
		}
2396
2397
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
2398
		Jetpack::state( 'module', $module );
2399
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
2400
2401
		Jetpack::catch_errors( true );
2402
		ob_start();
2403
		require Jetpack::get_module_path( $module );
2404
		/** This action is documented in class.jetpack.php */
2405
		do_action( 'jetpack_activate_module', $module );
2406
		$active[] = $module;
2407
		Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2408
		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...
2409
		Jetpack::state( 'message', 'module_activated' );
2410
		Jetpack::state( 'module', $module );
2411
		ob_end_clean();
2412
		Jetpack::catch_errors( false );
2413
2414
		// A flag for Jump Start so it's not shown again. Only set if it hasn't been yet.
2415 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2416
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2417
2418
			//Jump start is being dismissed send data to MC Stats
2419
			$jetpack->stat( 'jumpstart', 'manual,'.$module );
2420
2421
			$jetpack->do_stats( 'server_side' );
2422
		}
2423
2424
		if ( $redirect ) {
2425
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2426
		}
2427
		if ( $exit ) {
2428
			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...
2429
		}
2430
	}
2431
2432
	function activate_module_actions( $module ) {
2433
		/**
2434
		 * Fires when a module is activated.
2435
		 * The dynamic part of the filter, $module, is the module slug.
2436
		 *
2437
		 * @since 1.9.0
2438
		 *
2439
		 * @param string $module Module slug.
2440
		 */
2441
		do_action( "jetpack_activate_module_$module", $module );
2442
	}
2443
2444
	public static function deactivate_module( $module ) {
2445
		/**
2446
		 * Fires when a module is deactivated.
2447
		 *
2448
		 * @since 1.9.0
2449
		 *
2450
		 * @param string $module Module slug.
2451
		 */
2452
		do_action( 'jetpack_pre_deactivate_module', $module );
2453
2454
		$jetpack = Jetpack::init();
2455
2456
		$active = Jetpack::get_active_modules();
2457
		$new    = array_filter( array_diff( $active, (array) $module ) );
2458
2459
		/**
2460
		 * Fires when a module is deactivated.
2461
		 * The dynamic part of the filter, $module, is the module slug.
2462
		 *
2463
		 * @since 1.9.0
2464
		 *
2465
		 * @param string $module Module slug.
2466
		 */
2467
		do_action( "jetpack_deactivate_module_$module", $module );
2468
2469
		// A flag for Jump Start so it's not shown again.
2470 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2471
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2472
2473
			//Jump start is being dismissed send data to MC Stats
2474
			$jetpack->stat( 'jumpstart', 'manual,deactivated-'.$module );
2475
2476
			$jetpack->do_stats( 'server_side' );
2477
		}
2478
2479
		return Jetpack_Options::update_option( 'active_modules', array_unique( $new ) );
2480
	}
2481
2482
	public static function enable_module_configurable( $module ) {
2483
		$module = Jetpack::get_module_slug( $module );
2484
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
2485
	}
2486
2487
	public static function module_configuration_url( $module ) {
2488
		$module = Jetpack::get_module_slug( $module );
2489
		return Jetpack::admin_url( array( 'page' => 'jetpack', 'configure' => $module ) );
2490
	}
2491
2492
	public static function module_configuration_load( $module, $method ) {
2493
		$module = Jetpack::get_module_slug( $module );
2494
		add_action( 'jetpack_module_configuration_load_' . $module, $method );
2495
	}
2496
2497
	public static function module_configuration_head( $module, $method ) {
2498
		$module = Jetpack::get_module_slug( $module );
2499
		add_action( 'jetpack_module_configuration_head_' . $module, $method );
2500
	}
2501
2502
	public static function module_configuration_screen( $module, $method ) {
2503
		$module = Jetpack::get_module_slug( $module );
2504
		add_action( 'jetpack_module_configuration_screen_' . $module, $method );
2505
	}
2506
2507
	public static function module_configuration_activation_screen( $module, $method ) {
2508
		$module = Jetpack::get_module_slug( $module );
2509
		add_action( 'display_activate_module_setting_' . $module, $method );
2510
	}
2511
2512
/* Installation */
2513
2514
	public static function bail_on_activation( $message, $deactivate = true ) {
2515
?>
2516
<!doctype html>
2517
<html>
2518
<head>
2519
<meta charset="<?php bloginfo( 'charset' ); ?>">
2520
<style>
2521
* {
2522
	text-align: center;
2523
	margin: 0;
2524
	padding: 0;
2525
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
2526
}
2527
p {
2528
	margin-top: 1em;
2529
	font-size: 18px;
2530
}
2531
</style>
2532
<body>
2533
<p><?php echo esc_html( $message ); ?></p>
2534
</body>
2535
</html>
2536
<?php
2537
		if ( $deactivate ) {
2538
			$plugins = get_option( 'active_plugins' );
2539
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
2540
			$update  = false;
2541
			foreach ( $plugins as $i => $plugin ) {
2542
				if ( $plugin === $jetpack ) {
2543
					$plugins[$i] = false;
2544
					$update = true;
2545
				}
2546
			}
2547
2548
			if ( $update ) {
2549
				update_option( 'active_plugins', array_filter( $plugins ) );
2550
			}
2551
		}
2552
		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...
2553
	}
2554
2555
	/**
2556
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
2557
	 * @static
2558
	 */
2559
	public static function plugin_activation( $network_wide ) {
2560
		Jetpack_Options::update_option( 'activated', 1 );
2561
2562
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
2563
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
2564
		}
2565
2566
		if ( $network_wide )
2567
			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...
2568
2569
		Jetpack::plugin_initialize();
2570
	}
2571
	/**
2572
	 * Runs before bumping version numbers up to a new version
2573
	 * @param  (string) $version    Version:timestamp
2574
	 * @param  (string) $old_version Old Version:timestamp or false if not set yet.
2575
	 * @return null              [description]
2576
	 */
2577
	public static function do_version_bump( $version, $old_version ) {
2578
2579
		if ( ! $old_version ) { // For new sites
2580
			// Setting up jetpack manage
2581
			Jetpack::activate_manage();
2582
		}
2583
	}
2584
2585
	/**
2586
	 * Sets the internal version number and activation state.
2587
	 * @static
2588
	 */
2589
	public static function plugin_initialize() {
2590
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
2591
			Jetpack_Options::update_option( 'activated', 2 );
2592
		}
2593
2594 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
2595
			$version = $old_version = JETPACK__VERSION . ':' . time();
2596
			/** This action is documented in class.jetpack.php */
2597
			do_action( 'updating_jetpack_version', $version, false );
2598
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2599
		}
2600
2601
		Jetpack::load_modules();
2602
2603
		Jetpack_Options::delete_option( 'do_activate' );
2604
	}
2605
2606
	/**
2607
	 * Removes all connection options
2608
	 * @static
2609
	 */
2610
	public static function plugin_deactivation( ) {
2611
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
2612
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
2613
			Jetpack_Network::init()->deactivate();
2614
		} else {
2615
			Jetpack::disconnect( false );
2616
			//Jetpack_Heartbeat::init()->deactivate();
2617
		}
2618
	}
2619
2620
	/**
2621
	 * Disconnects from the Jetpack servers.
2622
	 * Forgets all connection details and tells the Jetpack servers to do the same.
2623
	 * @static
2624
	 */
2625
	public static function disconnect( $update_activated_state = true ) {
2626
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
2627
		Jetpack::clean_nonces( true );
2628
2629
		Jetpack::load_xml_rpc_client();
2630
		$xml = new Jetpack_IXR_Client();
2631
		$xml->query( 'jetpack.deregister' );
2632
2633
		Jetpack_Options::delete_option(
2634
			array(
2635
				'register',
2636
				'blog_token',
2637
				'user_token',
2638
				'user_tokens',
2639
				'master_user',
2640
				'time_diff',
2641
				'fallback_no_verify_ssl_certs',
2642
			)
2643
		);
2644
2645
		if ( $update_activated_state ) {
2646
			Jetpack_Options::update_option( 'activated', 4 );
2647
		}
2648
2649
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
2650
		// Check then record unique disconnection if site has never been disconnected previously
2651
		if ( -1 == $jetpack_unique_connection['disconnected'] ) {
2652
			$jetpack_unique_connection['disconnected'] = 1;
2653
		}
2654
		else {
2655
			if ( 0 == $jetpack_unique_connection['disconnected'] ) {
2656
				//track unique disconnect
2657
				$jetpack = Jetpack::init();
2658
2659
				$jetpack->stat( 'connections', 'unique-disconnect' );
2660
				$jetpack->do_stats( 'server_side' );
2661
			}
2662
			// increment number of times disconnected
2663
			$jetpack_unique_connection['disconnected'] += 1;
2664
		}
2665
2666
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
2667
2668
		// Disable the Heartbeat cron
2669
		Jetpack_Heartbeat::init()->deactivate();
2670
	}
2671
2672
	/**
2673
	 * Unlinks the current user from the linked WordPress.com user
2674
	 */
2675
	public static function unlink_user( $user_id = null ) {
2676
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
2677
			return false;
2678
2679
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
2680
2681
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
2682
			return false;
2683
2684
		if ( ! isset( $tokens[ $user_id ] ) )
2685
			return false;
2686
2687
		Jetpack::load_xml_rpc_client();
2688
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
2689
		$xml->query( 'jetpack.unlink_user', $user_id );
2690
2691
		unset( $tokens[ $user_id ] );
2692
2693
		Jetpack_Options::update_option( 'user_tokens', $tokens );
2694
2695
		return true;
2696
	}
2697
2698
	/**
2699
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
2700
	 */
2701
	public static function try_registration() {
2702
		// Let's get some testing in beta versions and such.
2703
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
2704
			// Before attempting to connect, let's make sure that the domains are viable.
2705
			$domains_to_check = array_unique( array(
2706
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
2707
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
2708
			) );
2709
			foreach ( $domains_to_check as $domain ) {
2710
				$result = Jetpack_Data::is_usable_domain( $domain );
2711
				if ( is_wp_error( $result ) ) {
2712
					return $result;
2713
				}
2714
			}
2715
		}
2716
2717
		$result = Jetpack::register();
2718
2719
		// If there was an error with registration and the site was not registered, record this so we can show a message.
2720
		if ( ! $result || is_wp_error( $result ) ) {
2721
			return $result;
2722
		} else {
2723
			return true;
2724
		}
2725
	}
2726
2727
	/**
2728
	 * Tracking an internal event log. Try not to put too much chaff in here.
2729
	 *
2730
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
2731
	 */
2732
	public static function log( $code, $data = null ) {
2733
		// only grab the latest 200 entries
2734
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
2735
2736
		// Append our event to the log
2737
		$log_entry = array(
2738
			'time'    => time(),
2739
			'user_id' => get_current_user_id(),
2740
			'blog_id' => Jetpack_Options::get_option( 'id' ),
2741
			'code'    => $code,
2742
		);
2743
		// Don't bother storing it unless we've got some.
2744
		if ( ! is_null( $data ) ) {
2745
			$log_entry['data'] = $data;
2746
		}
2747
		$log[] = $log_entry;
2748
2749
		// Try add_option first, to make sure it's not autoloaded.
2750
		// @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...
2751
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
2752
			Jetpack_Options::update_option( 'log', $log );
2753
		}
2754
2755
		/**
2756
		 * Fires when Jetpack logs an internal event.
2757
		 *
2758
		 * @since 3.0.0
2759
		 *
2760
		 * @param array $log_entry {
2761
		 *	Array of details about the log entry.
2762
		 *
2763
		 *	@param string time Time of the event.
2764
		 *	@param int user_id ID of the user who trigerred the event.
2765
		 *	@param int blog_id Jetpack Blog ID.
2766
		 *	@param string code Unique name for the event.
2767
		 *	@param string data Data about the event.
2768
		 * }
2769
		 */
2770
		do_action( 'jetpack_log_entry', $log_entry );
2771
	}
2772
2773
	/**
2774
	 * Get the internal event log.
2775
	 *
2776
	 * @param $event (string) - only return the specific log events
2777
	 * @param $num   (int)    - get specific number of latest results, limited to 200
2778
	 *
2779
	 * @return array of log events || WP_Error for invalid params
2780
	 */
2781
	public static function get_log( $event = false, $num = false ) {
2782
		if ( $event && ! is_string( $event ) ) {
2783
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
2784
		}
2785
2786
		if ( $num && ! is_numeric( $num ) ) {
2787
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
2788
		}
2789
2790
		$entire_log = Jetpack_Options::get_option( 'log', array() );
2791
2792
		// If nothing set - act as it did before, otherwise let's start customizing the output
2793
		if ( ! $num && ! $event ) {
2794
			return $entire_log;
2795
		} else {
2796
			$entire_log = array_reverse( $entire_log );
2797
		}
2798
2799
		$custom_log_output = array();
2800
2801
		if ( $event ) {
2802
			foreach ( $entire_log as $log_event ) {
2803
				if ( $event == $log_event[ 'code' ] ) {
2804
					$custom_log_output[] = $log_event;
2805
				}
2806
			}
2807
		} else {
2808
			$custom_log_output = $entire_log;
2809
		}
2810
2811
		if ( $num ) {
2812
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
2813
		}
2814
2815
		return $custom_log_output;
2816
	}
2817
2818
	/**
2819
	 * Log modification of important settings.
2820
	 */
2821
	public static function log_settings_change( $option, $old_value, $value ) {
2822
		switch( $option ) {
2823
			case 'jetpack_sync_non_public_post_stati':
2824
				self::log( $option, $value );
2825
				break;
2826
		}
2827
	}
2828
2829
	/* Admin Pages */
2830
2831
	function admin_init() {
2832
		// If the plugin is not connected, display a connect message.
2833
		if (
2834
			// the plugin was auto-activated and needs its candy
2835
			Jetpack_Options::get_option( 'do_activate' )
2836
		||
2837
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
2838
			! Jetpack_Options::get_option( 'activated' )
2839
		) {
2840
			Jetpack::plugin_initialize();
2841
		}
2842
2843
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
2844
			if ( 4 != Jetpack_Options::get_option( 'activated' ) ) {
2845
				// Show connect notice on dashboard and plugins pages
2846
				add_action( 'load-index.php', array( $this, 'prepare_connect_notice' ) );
2847
				add_action( 'load-plugins.php', array( $this, 'prepare_connect_notice' ) );
2848
			}
2849
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
2850
			// Upgrade: 1.1 -> 1.1.1
2851
			// Check and see if host can verify the Jetpack servers' SSL certificate
2852
			$args = array();
2853
			Jetpack_Client::_wp_remote_request(
2854
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
2855
				$args,
2856
				true
2857
			);
2858
		} else {
2859
			// Show the notice on the Dashboard only for now
2860
2861
			add_action( 'load-index.php', array( $this, 'prepare_manage_jetpack_notice' ) );
2862
2863
			// Identity crisis notices
2864
			add_action( 'jetpack_notices', array( $this, 'alert_identity_crisis' ) );
2865
		}
2866
2867
		// If the plugin has just been disconnected from WP.com, show the survey notice
2868
		if ( isset( $_GET['disconnected'] ) && 'true' === $_GET['disconnected'] ) {
2869
			add_action( 'jetpack_notices', array( $this, 'disconnect_survey_notice' ) );
2870
		}
2871
2872
		if ( current_user_can( 'manage_options' ) && 'ALWAYS' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
2873
			add_action( 'admin_notices', array( $this, 'alert_required_ssl_fail' ) );
2874
		}
2875
2876
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
2877
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
2878
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
2879
2880
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
2881
			// Artificially throw errors in certain whitelisted cases during plugin activation
2882
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
2883
		}
2884
2885
		// Jetpack Manage Activation Screen from .com
2886
		Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
2887
	}
2888
2889
	function admin_body_class( $admin_body_class = '' ) {
2890
		$classes = explode( ' ', trim( $admin_body_class ) );
2891
2892
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
2893
2894
		$admin_body_class = implode( ' ', array_unique( $classes ) );
2895
		return " $admin_body_class ";
2896
	}
2897
2898
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
2899
		return $admin_body_class . ' jetpack-pagestyles ';
2900
	}
2901
2902
	function prepare_connect_notice() {
2903
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
2904
2905
		add_action( 'admin_notices', array( $this, 'admin_connect_notice' ) );
2906
2907
		if ( Jetpack::state( 'network_nag' ) )
2908
			add_action( 'network_admin_notices', array( $this, 'network_connect_notice' ) );
2909
	}
2910
	/**
2911
	 * Call this function if you want the Big Jetpack Manage Notice to show up.
2912
	 *
2913
	 * @return null
2914
	 */
2915
	function prepare_manage_jetpack_notice() {
2916
2917
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
2918
		add_action( 'admin_notices', array( $this, 'admin_jetpack_manage_notice' ) );
2919
	}
2920
2921
	function manage_activate_screen() {
2922
		include ( JETPACK__PLUGIN_DIR . 'modules/manage/activate-admin.php' );
2923
	}
2924
	/**
2925
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
2926
	 * This function artificially throws errors for such cases (whitelisted).
2927
	 *
2928
	 * @param string $plugin The activated plugin.
2929
	 */
2930
	function throw_error_on_activate_plugin( $plugin ) {
2931
		$active_modules = Jetpack::get_active_modules();
2932
2933
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
2934
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
2935
			$throw = false;
2936
2937
			// Try and make sure it really was the stats plugin
2938
			if ( ! class_exists( 'ReflectionFunction' ) ) {
2939
				if ( 'stats.php' == basename( $plugin ) ) {
2940
					$throw = true;
2941
				}
2942
			} else {
2943
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
2944
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
2945
					$throw = true;
2946
				}
2947
			}
2948
2949
			if ( $throw ) {
2950
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
2951
			}
2952
		}
2953
	}
2954
2955
	function intercept_plugin_error_scrape_init() {
2956
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
2957
	}
2958
2959
	function intercept_plugin_error_scrape( $action, $result ) {
2960
		if ( ! $result ) {
2961
			return;
2962
		}
2963
2964
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
2965
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
2966
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
2967
			}
2968
		}
2969
	}
2970
2971
	function add_remote_request_handlers() {
2972
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
2973
	}
2974
2975
	function remote_request_handlers() {
2976
		switch ( current_filter() ) {
2977
		case 'wp_ajax_nopriv_jetpack_upload_file' :
2978
			$response = $this->upload_handler();
2979
			break;
2980
		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...
2981
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
2982
			break;
2983
		}
2984
2985
		if ( ! $response ) {
2986
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
2987
		}
2988
2989
		if ( is_wp_error( $response ) ) {
2990
			$status_code       = $response->get_error_data();
2991
			$error             = $response->get_error_code();
2992
			$error_description = $response->get_error_message();
2993
2994
			if ( ! is_int( $status_code ) ) {
2995
				$status_code = 400;
2996
			}
2997
2998
			status_header( $status_code );
2999
			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...
3000
		}
3001
3002
		status_header( 200 );
3003
		if ( true === $response ) {
3004
			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...
3005
		}
3006
3007
		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...
3008
	}
3009
3010
	function upload_handler() {
3011
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3012
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
3013
		}
3014
3015
		$user = wp_authenticate( '', '' );
3016
		if ( ! $user || is_wp_error( $user ) ) {
3017
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
3018
		}
3019
3020
		wp_set_current_user( $user->ID );
3021
3022
		if ( ! current_user_can( 'upload_files' ) ) {
3023
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3024
		}
3025
3026
		if ( empty( $_FILES ) ) {
3027
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3028
		}
3029
3030
		foreach ( array_keys( $_FILES ) as $files_key ) {
3031
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3032
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3033
			}
3034
		}
3035
3036
		$media_keys = array_keys( $_FILES['media'] );
3037
3038
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
3039
		if ( ! $token || is_wp_error( $token ) ) {
3040
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3041
		}
3042
3043
		$uploaded_files = array();
3044
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3045
		unset( $GLOBALS['post'] );
3046
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3047
			$file = array();
3048
			foreach ( $media_keys as $media_key ) {
3049
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3050
			}
3051
3052
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3053
3054
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3055
			if ( $hmac_provided !== $hmac_file ) {
3056
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3057
				continue;
3058
			}
3059
3060
			$_FILES['.jetpack.upload.'] = $file;
3061
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3062
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3063
				$post_id = 0;
3064
			}
3065
			$attachment_id = media_handle_upload(
3066
				'.jetpack.upload.',
3067
				$post_id,
3068
				array(),
3069
				array(
3070
					'action' => 'jetpack_upload_file',
3071
				)
3072
			);
3073
3074
			if ( ! $attachment_id ) {
3075
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3076
			} elseif ( is_wp_error( $attachment_id ) ) {
3077
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3078
			} else {
3079
				$attachment = get_post( $attachment_id );
3080
				$uploaded_files[$index] = (object) array(
3081
					'id'   => (string) $attachment_id,
3082
					'file' => $attachment->post_title,
3083
					'url'  => wp_get_attachment_url( $attachment_id ),
3084
					'type' => $attachment->post_mime_type,
3085
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3086
				);
3087
			}
3088
		}
3089
		if ( ! is_null( $global_post ) ) {
3090
			$GLOBALS['post'] = $global_post;
3091
		}
3092
3093
		return $uploaded_files;
3094
	}
3095
3096
	/**
3097
	 * Add help to the Jetpack page
3098
	 *
3099
	 * @since Jetpack (1.2.3)
3100
	 * @return false if not the Jetpack page
3101
	 */
3102
	function admin_help() {
3103
		$current_screen = get_current_screen();
3104
3105
		// Overview
3106
		$current_screen->add_help_tab(
3107
			array(
3108
				'id'		=> 'home',
3109
				'title'		=> __( 'Home', 'jetpack' ),
3110
				'content'	=>
3111
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3112
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3113
					'<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>',
3114
			)
3115
		);
3116
3117
		// Screen Content
3118
		if ( current_user_can( 'manage_options' ) ) {
3119
			$current_screen->add_help_tab(
3120
				array(
3121
					'id'		=> 'settings',
3122
					'title'		=> __( 'Settings', 'jetpack' ),
3123
					'content'	=>
3124
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3125
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3126
						'<ol>' .
3127
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3128
							'<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>' .
3129
						'</ol>' .
3130
						'<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>'
3131
				)
3132
			);
3133
		}
3134
3135
		// Help Sidebar
3136
		$current_screen->set_help_sidebar(
3137
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3138
			'<p><a href="http://jetpack.me/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
3139
			'<p><a href="http://jetpack.me/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3140
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3141
		);
3142
	}
3143
3144
	function admin_menu_css() {
3145
		wp_enqueue_style( 'jetpack-icons' );
3146
	}
3147
3148
	function admin_menu_order() {
3149
		return true;
3150
	}
3151
3152 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3153
		$jp_menu_order = array();
3154
3155
		foreach ( $menu_order as $index => $item ) {
3156
			if ( $item != 'jetpack' ) {
3157
				$jp_menu_order[] = $item;
3158
			}
3159
3160
			if ( $index == 0 ) {
3161
				$jp_menu_order[] = 'jetpack';
3162
			}
3163
		}
3164
3165
		return $jp_menu_order;
3166
	}
3167
3168
	function admin_head() {
3169 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) )
3170
			/** This action is documented in class.jetpack-admin-page.php */
3171
			do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
3172
	}
3173
3174
	function admin_banner_styles() {
3175
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3176
3177
		wp_enqueue_style( 'jetpack', plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION . '-20121016' );
3178
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3179
		wp_style_add_data( 'jetpack', 'suffix', $min );
3180
	}
3181
3182
	function admin_scripts() {
3183
		wp_enqueue_script( 'jetpack-js', plugins_url( '_inc/jp.js', JETPACK__PLUGIN_FILE ), array( 'jquery', 'wp-util' ), JETPACK__VERSION . '-20121111' );
3184
		wp_localize_script(
3185
			'jetpack-js',
3186
			'jetpackL10n',
3187
			array(
3188
				'ays_disconnect' => "This will deactivate all Jetpack modules.\nAre you sure you want to disconnect?",
3189
				'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?",
3190
				'ays_dismiss'    => "This will deactivate Jetpack.\nAre you sure you want to deactivate Jetpack?",
3191
			)
3192
		);
3193
		add_action( 'admin_footer', array( $this, 'do_stats' ) );
3194
	}
3195
3196
	function plugin_action_links( $actions ) {
3197
3198
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), __( 'Jetpack', 'jetpack' ) ) );
3199
3200
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
3201
			return array_merge(
3202
				$jetpack_home,
3203
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack_modules' ), __( 'Settings', 'jetpack' ) ) ),
3204
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
3205
				$actions
3206
				);
3207
			}
3208
3209
		return array_merge( $jetpack_home, $actions );
3210
	}
3211
3212
	function admin_connect_notice() {
3213
		// Don't show the connect notice anywhere but the plugins.php after activating
3214
		$current = get_current_screen();
3215
		if ( 'plugins' !== $current->parent_base )
3216
			return;
3217
3218
		if ( ! current_user_can( 'jetpack_connect' ) )
3219
			return;
3220
3221
		$dismiss_and_deactivate_url = wp_nonce_url( Jetpack::admin_url( '?page=jetpack&jetpack-notice=dismiss' ), 'jetpack-deactivate' );
3222
		?>
3223
		<div id="message" class="updated jetpack-message jp-banner" style="display:block !important;">
3224
			<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>
3225
			<?php if ( in_array( Jetpack_Options::get_option( 'activated' ) , array( 1, 2, 3 ) ) ) : ?>
3226
				<div class="jp-banner__content is-connection">
3227
					<h2><?php _e( 'Your Jetpack is almost ready!', 'jetpack' ); ?></h2>
3228
					<p><?php _e( 'Connect now to enable features like Stats, Likes, and Social Sharing.', 'jetpack' ); ?></p>
3229
				</div>
3230
				<div class="jp-banner__action-container is-connection">
3231
						<a href="<?php echo $this->build_connect_url() ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Connect to WordPress.com', 'jetpack' ); ?></a>
3232
				</div>
3233 View Code Duplication
			<?php else : ?>
3234
				<div class="jp-banner__content">
3235
					<h2><?php _e( 'Jetpack is installed!', 'jetpack' ) ?></h2>
3236
					<p><?php _e( 'It\'s ready to bring awesome, WordPress.com cloud-powered features to your site.', 'jetpack' ) ?></p>
3237
				</div>
3238
				<div class="jp-banner__action-container">
3239
					<a href="<?php echo Jetpack::admin_url() ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Learn More', 'jetpack' ); ?></a>
3240
				</div>
3241
			<?php endif; ?>
3242
		</div>
3243
3244
		<?php
3245
	}
3246
3247
	/**
3248
	 * This is the first banner
3249
	 * It should be visible only to user that can update the option
3250
	 * Are not connected
3251
	 *
3252
	 * @return null
3253
	 */
3254
	function admin_jetpack_manage_notice() {
3255
		$screen = get_current_screen();
3256
3257
		// Don't show the connect notice on the jetpack settings page.
3258
		if ( ! in_array( $screen->base, array( 'dashboard' ) ) || $screen->is_network || $screen->action )
3259
			return;
3260
3261
		// Only show it if don't have the managment option set.
3262
		// And not dismissed it already.
3263
		if ( ! $this->can_display_jetpack_manage_notice() || Jetpack_Options::get_option( 'dismissed_manage_banner' ) ) {
3264
			return;
3265
		}
3266
3267
		$opt_out_url = $this->opt_out_jetpack_manage_url();
3268
		$opt_in_url  = $this->opt_in_jetpack_manage_url();
3269
		/**
3270
		 * I think it would be great to have different wordsing depending on where you are
3271
		 * for example if we show the notice on dashboard and a different one if we show it on Plugins screen
3272
		 * etc..
3273
		 */
3274
3275
		?>
3276
		<div id="message" class="updated jetpack-message jp-banner is-opt-in" style="display:block !important;">
3277
			<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>
3278
			<div class="jp-banner__content">
3279
				<h2><?php esc_html_e( 'New in Jetpack: Centralized Site Management', 'jetpack' ); ?></h2>
3280
				<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>
3281
			</div>
3282
			<div class="jp-banner__action-container is-opt-in">
3283
				<a href="<?php echo esc_url( $opt_in_url ); ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Activate now', 'jetpack' ); ?></a>
3284
			</div>
3285
		</div>
3286
		<?php
3287
	}
3288
3289
	/**
3290
	 * Returns the url that the user clicks to remove the notice for the big banner
3291
	 * @return (string)
3292
	 */
3293
	function opt_out_jetpack_manage_url() {
3294
		$referer = '&_wp_http_referer=' . add_query_arg( '_wp_http_referer', null );
3295
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-out' . $referer ), 'jetpack_manage_banner_opt_out' );
3296
	}
3297
	/**
3298
	 * Returns the url that the user clicks to opt in to Jetpack Manage
3299
	 * @return (string)
3300
	 */
3301
	function opt_in_jetpack_manage_url() {
3302
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-in' ), 'jetpack_manage_banner_opt_in' );
3303
	}
3304
3305
	function opt_in_jetpack_manage_notice() {
3306
		?>
3307
		<div class="wrap">
3308
			<div id="message" class="jetpack-message is-opt-in">
3309
				<?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' ); ?>
3310
			</div>
3311
		</div>
3312
		<?php
3313
3314
	}
3315
	/**
3316
	 * Determines whether to show the notice of not true = display notice
3317
	 * @return (bool)
3318
	 */
3319
	function can_display_jetpack_manage_notice() {
3320
		// never display the notice to users that can't do anything about it anyways
3321
		if( ! current_user_can( 'jetpack_manage_modules' ) )
3322
			return false;
3323
3324
		// don't display if we are in development more
3325
		if( Jetpack::is_development_mode() ) {
3326
			return false;
3327
		}
3328
		// don't display if the site is private
3329
		if(  ! Jetpack_Options::get_option( 'public' ) )
3330
			return false;
3331
3332
		/**
3333
		 * Should the Jetpack Remote Site Management notice be displayed.
3334
		 *
3335
		 * @since 3.3.0
3336
		 *
3337
		 * @param bool ! self::is_module_active( 'manage' ) Is the Manage module inactive.
3338
		 */
3339
		return apply_filters( 'can_display_jetpack_manage_notice', ! self::is_module_active( 'manage' ) );
3340
	}
3341
3342
	function network_connect_notice() {
3343
		?>
3344
		<div id="message" class="updated jetpack-message">
3345
			<div class="squeezer">
3346
				<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>
3347
			</div>
3348
		</div>
3349
		<?php
3350
	}
3351
3352
	public static function jetpack_comment_notice() {
3353
		if ( in_array( 'comments', Jetpack::get_active_modules() ) ) {
3354
			return '';
3355
		}
3356
3357
		$jetpack_old_version = explode( ':', Jetpack_Options::get_option( 'old_version' ) );
3358
		$jetpack_new_version = explode( ':', Jetpack_Options::get_option( 'version' ) );
3359
3360
		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...
3361
			if ( version_compare( $jetpack_old_version[0], '1.4', '>=' ) ) {
3362
				return '';
3363
			}
3364
		}
3365
3366
		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...
3367
			if ( version_compare( $jetpack_new_version[0], '1.4-something', '<' ) ) {
3368
				return '';
3369
			}
3370
		}
3371
3372
		return '<br /><br />' . sprintf(
3373
			__( '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' ),
3374
			wp_nonce_url(
3375
				Jetpack::admin_url(
3376
					array(
3377
						'page'   => 'jetpack',
3378
						'action' => 'activate',
3379
						'module' => 'comments',
3380
					)
3381
				),
3382
				'jetpack_activate-comments'
3383
			),
3384
			__( 'click here', 'jetpack' )
3385
		);
3386
	}
3387
3388
	/**
3389
	 * Show the survey link when the user has just disconnected Jetpack.
3390
	 */
3391
	function disconnect_survey_notice() {
3392
		?>
3393
		<div class="wrap">
3394
			<div id="message" class="jetpack-message stay-visible">
3395
				<div class="squeezer">
3396
					<h2>
3397
						<?php _e( 'You have successfully disconnected Jetpack.', 'jetpack' ); ?>
3398
						<br />
3399
						<?php echo sprintf(
3400
							__( 'Would you tell us why? Just <a href="%1$s" target="%2$s">answering two simple questions</a> would help us improve Jetpack.', 'jetpack' ),
3401
							'https://jetpack.me/survey-disconnected/',
3402
							'_blank'
3403
						); ?>
3404
					</h2>
3405
				</div>
3406
			</div>
3407
		</div>
3408
		<?php
3409
	}
3410
3411
	/*
3412
	 * Registration flow:
3413
	 * 1 - ::admin_page_load() action=register
3414
	 * 2 - ::try_registration()
3415
	 * 3 - ::register()
3416
	 *     - Creates jetpack_register option containing two secrets and a timestamp
3417
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
3418
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
3419
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
3420
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
3421
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
3422
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
3423
	 *       jetpack_id, jetpack_secret, jetpack_public
3424
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
3425
	 * 4 - redirect to https://jetpack.wordpress.com/jetpack.authorize/1/
3426
	 * 5 - user logs in with WP.com account
3427
	 * 6 - redirect to this site's wp-admin/index.php?page=jetpack&action=authorize with
3428
	 *     code <-- OAuth2 style authorization code
3429
	 * 7 - ::admin_page_load() action=authorize
3430
	 * 8 - Jetpack_Client_Server::authorize()
3431
	 * 9 - Jetpack_Client_Server::get_token()
3432
	 * 10- GET https://jetpack.wordpress.com/jetpack.token/1/ with
3433
	 *     client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
3434
	 * 11- which responds with
3435
	 *     access_token, token_type, scope
3436
	 * 12- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
3437
	 * 13- Jetpack::activate_default_modules()
3438
	 *     Deactivates deprecated plugins
3439
	 *     Activates all default modules
3440
	 *     Catches errors: redirects to wp-admin/index.php?page=jetpack state:error=something
3441
	 * 14- redirect to this site's wp-admin/index.php?page=jetpack with state:message=authorized
3442
	 *     Done!
3443
	 */
3444
3445
	/**
3446
	 * Handles the page load events for the Jetpack admin page
3447
	 */
3448
	function admin_page_load() {
3449
		$error = false;
3450
3451
		// Make sure we have the right body class to hook stylings for subpages off of.
3452
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
3453
3454
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
3455
			// Should only be used in intermediate redirects to preserve state across redirects
3456
			Jetpack::restate();
3457
		}
3458
3459
		if ( isset( $_GET['connect_url_redirect'] ) ) {
3460
			// User clicked in the iframe to link their accounts
3461
			if ( ! Jetpack::is_user_connected() ) {
3462
				$connect_url = $this->build_connect_url( true );
3463
				if ( isset( $_GET['notes_iframe'] ) )
3464
					$connect_url .= '&notes_iframe';
3465
				wp_redirect( $connect_url );
3466
				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...
3467
			} else {
3468
				Jetpack::state( 'message', 'already_authorized' );
3469
				wp_safe_redirect( Jetpack::admin_url() );
3470
				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...
3471
			}
3472
		}
3473
3474
3475
		if ( isset( $_GET['action'] ) ) {
3476
			switch ( $_GET['action'] ) {
3477
			case 'authorize' :
3478
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
3479
					Jetpack::state( 'message', 'already_authorized' );
3480
					wp_safe_redirect( Jetpack::admin_url() );
3481
					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...
3482
				}
3483
				Jetpack::log( 'authorize' );
3484
				$client_server = new Jetpack_Client_Server;
3485
				$client_server->authorize();
3486
				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...
3487
			case 'register' :
3488
				if ( ! current_user_can( 'jetpack_connect' ) ) {
3489
					$error = 'cheatin';
3490
					break;
3491
				}
3492
				check_admin_referer( 'jetpack-register' );
3493
				Jetpack::log( 'register' );
3494
				Jetpack::maybe_set_version_option();
3495
				$registered = Jetpack::try_registration();
3496
				if ( is_wp_error( $registered ) ) {
3497
					$error = $registered->get_error_code();
3498
					Jetpack::state( 'error_description', $registered->get_error_message() );
3499
					break;
3500
				}
3501
3502
				wp_redirect( $this->build_connect_url( true ) );
3503
				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...
3504
			case 'activate' :
3505
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3506
					$error = 'cheatin';
3507
					break;
3508
				}
3509
3510
				$module = stripslashes( $_GET['module'] );
3511
				check_admin_referer( "jetpack_activate-$module" );
3512
				Jetpack::log( 'activate', $module );
3513
				Jetpack::activate_module( $module );
3514
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3515
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3516
				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...
3517
			case 'activate_default_modules' :
3518
				check_admin_referer( 'activate_default_modules' );
3519
				Jetpack::log( 'activate_default_modules' );
3520
				Jetpack::restate();
3521
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
3522
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
3523
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
3524
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
3525
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3526
				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...
3527
			case 'disconnect' :
3528
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3529
					$error = 'cheatin';
3530
					break;
3531
				}
3532
3533
				check_admin_referer( 'jetpack-disconnect' );
3534
				Jetpack::log( 'disconnect' );
3535
				Jetpack::disconnect();
3536
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
3537
				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...
3538
			case 'reconnect' :
3539
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
3540
					$error = 'cheatin';
3541
					break;
3542
				}
3543
3544
				check_admin_referer( 'jetpack-reconnect' );
3545
				Jetpack::log( 'reconnect' );
3546
				$this->disconnect();
3547
				wp_redirect( $this->build_connect_url( true ) );
3548
				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...
3549 View Code Duplication
			case 'deactivate' :
3550
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
3551
					$error = 'cheatin';
3552
					break;
3553
				}
3554
3555
				$modules = stripslashes( $_GET['module'] );
3556
				check_admin_referer( "jetpack_deactivate-$modules" );
3557
				foreach ( explode( ',', $modules ) as $module ) {
3558
					Jetpack::log( 'deactivate', $module );
3559
					Jetpack::deactivate_module( $module );
3560
					Jetpack::state( 'message', 'module_deactivated' );
3561
				}
3562
				Jetpack::state( 'module', $modules );
3563
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3564
				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...
3565
			case 'unlink' :
3566
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
3567
				check_admin_referer( 'jetpack-unlink' );
3568
				Jetpack::log( 'unlink' );
3569
				$this->unlink_user();
3570
				Jetpack::state( 'message', 'unlinked' );
3571
				if ( 'sub-unlink' == $redirect ) {
3572
					wp_safe_redirect( admin_url() );
3573
				} else {
3574
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
3575
				}
3576
				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...
3577
			default:
3578
				/**
3579
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
3580
				 *
3581
				 * @since 2.6.0
3582
				 *
3583
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
3584
				 */
3585
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
3586
			}
3587
		}
3588
3589
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
3590
			self::activate_new_modules( true );
3591
		}
3592
3593
		switch ( $error ) {
3594
		case 'cheatin' :
3595
			$this->error = __( 'Cheatin&#8217; uh?', 'jetpack' );
3596
			break;
3597
		case 'access_denied' :
3598
			$this->error = __( 'You need to authorize the Jetpack connection between your site and WordPress.com to enable the awesome features.', 'jetpack' );
3599
			break;
3600
		case 'wrong_state' :
3601
			$this->error = __( 'Don&#8217;t cross the streams!  You need to stay logged in to your WordPress blog while you authorize Jetpack.', 'jetpack' );
3602
			break;
3603
		case 'invalid_client' :
3604
			// @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...
3605
			$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' );
3606
			break;
3607
		case 'invalid_grant' :
3608
			$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' );
3609
			break;
3610
		case 'site_inaccessible' :
3611
		case 'site_requires_authorization' :
3612
			$this->error = sprintf( __( 'Your website needs to be publicly accessible to use Jetpack: %s', 'jetpack' ), "<code>$error</code>" );
3613
			break;
3614
		case 'module_activation_failed' :
3615
			$module = Jetpack::state( 'module' );
3616
			if ( ! empty( $module ) && $mod = Jetpack::get_module( $module ) ) {
3617
				$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'] );
3618
				if ( isset( $this->plugins_to_deactivate[$module] ) ) {
3619
					$this->error .= ' ' . sprintf( __( 'Do you still have the %s plugin installed?', 'jetpack' ), $this->plugins_to_deactivate[$module][1] );
3620
				}
3621
			} else {
3622
				$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' );
3623
			}
3624
			if ( $php_errors = Jetpack::state( 'php_errors' ) ) {
3625
				$this->error .= "<br />\n";
3626
				$this->error .= $php_errors;
3627
			}
3628
			break;
3629
		case 'master_user_required' :
3630
			$module = Jetpack::state( 'module' );
3631
			$module_name = '';
3632
			if ( ! empty( $module ) && $mod = Jetpack::get_module( $module ) ) {
3633
				$module_name = $mod['name'];
3634
			}
3635
3636
			$master_user = Jetpack_Options::get_option( 'master_user' );
3637
			$master_userdata = get_userdata( $master_user ) ;
3638
			if ( $master_userdata ) {
3639
				if ( ! in_array( $module, Jetpack::get_active_modules() ) ) {
3640
					$this->error = sprintf( __( '%s was not activated.' , 'jetpack' ), $module_name );
3641
				} else {
3642
					$this->error = sprintf( __( '%s was not deactivated.' , 'jetpack' ), $module_name );
3643
				}
3644
				$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 ) );
3645
3646
			} else {
3647
				$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 );
3648
			}
3649
			break;
3650
		case 'not_public' :
3651
			$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' );
3652
			break;
3653
		case 'wpcom_408' :
3654
		case 'wpcom_5??' :
3655
		case 'wpcom_bad_response' :
3656
		case 'wpcom_outage' :
3657
			$this->error = __( 'WordPress.com is currently having problems and is unable to fuel up your Jetpack.  Please try again later.', 'jetpack' );
3658
			break;
3659
		case 'register_http_request_failed' :
3660
		case 'token_http_request_failed' :
3661
			$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>" );
3662
			break;
3663
		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...
3664
			if ( empty( $error ) ) {
3665
				break;
3666
			}
3667
			$error = trim( substr( strip_tags( $error ), 0, 20 ) );
3668
			// no break: fall through
3669
		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...
3670
		case 'no_cap' :
3671
		case 'no_code' :
3672
		case 'no_state' :
3673
		case 'invalid_state' :
3674
		case 'invalid_request' :
3675
		case 'invalid_scope' :
3676
		case 'unsupported_response_type' :
3677
		case 'invalid_token' :
3678
		case 'no_token' :
3679
		case 'missing_secrets' :
3680
		case 'home_missing' :
3681
		case 'siteurl_missing' :
3682
		case 'gmt_offset_missing' :
3683
		case 'site_name_missing' :
3684
		case 'secret_1_missing' :
3685
		case 'secret_2_missing' :
3686
		case 'site_lang_missing' :
3687
		case 'home_malformed' :
3688
		case 'siteurl_malformed' :
3689
		case 'gmt_offset_malformed' :
3690
		case 'timezone_string_malformed' :
3691
		case 'site_name_malformed' :
3692
		case 'secret_1_malformed' :
3693
		case 'secret_2_malformed' :
3694
		case 'site_lang_malformed' :
3695
		case 'secrets_mismatch' :
3696
		case 'verify_secret_1_missing' :
3697
		case 'verify_secret_1_malformed' :
3698
		case 'verify_secrets_missing' :
3699
		case 'verify_secrets_mismatch' :
3700
			$error = esc_html( $error );
3701
			$this->error = sprintf( __( '<strong>Your Jetpack has a glitch.</strong>  We&#8217;re sorry for the inconvenience. Please try again later, if the issue continues please contact support with this message: %s', 'jetpack' ), "<code>$error</code>" );
3702
			if ( ! Jetpack::is_active() ) {
3703
				$this->error .= '<br />';
3704
				$this->error .= sprintf( __( 'Try connecting again.', 'jetpack' ) );
3705
			}
3706
			break;
3707
		}
3708
3709
		$message_code = Jetpack::state( 'message' );
3710
3711
		$active_state = Jetpack::state( 'activated_modules' );
3712
		if ( ! empty( $active_state ) ) {
3713
			$available    = Jetpack::get_available_modules();
3714
			$active_state = explode( ',', $active_state );
3715
			$active_state = array_intersect( $active_state, $available );
3716
			if ( count( $active_state ) ) {
3717
				foreach ( $active_state as $mod ) {
3718
					$this->stat( 'module-activated', $mod );
3719
				}
3720
			} else {
3721
				$active_state = false;
3722
			}
3723
		}
3724
		if( Jetpack::state( 'optin-manage' ) ) {
3725
			$activated_manage = $message_code;
3726
			$message_code = 'jetpack-manage';
3727
3728
		}
3729
		switch ( $message_code ) {
3730
		case 'modules_activated' :
3731
			$this->message = sprintf(
3732
				__( 'Welcome to <strong>Jetpack %s</strong>!', 'jetpack' ),
3733
				JETPACK__VERSION
3734
			);
3735
3736
			if ( $active_state ) {
3737
				$titles = array();
3738 View Code Duplication
				foreach ( $active_state as $mod ) {
3739
					if ( $mod_headers = Jetpack::get_module( $mod ) ) {
3740
						$titles[] = '<strong>' . preg_replace( '/\s+(?![^<>]++>)/', '&nbsp;', $mod_headers['name'] ) . '</strong>';
3741
					}
3742
				}
3743
				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...
3744
					$this->message .= '<br /><br />' . wp_sprintf( __( 'The following new modules have been activated: %l.', 'jetpack' ), $titles );
3745
				}
3746
			}
3747
3748
			if ( $reactive_state = Jetpack::state( 'reactivated_modules' ) ) {
3749
				$titles = array();
3750 View Code Duplication
				foreach ( explode( ',',  $reactive_state ) as $mod ) {
3751
					if ( $mod_headers = Jetpack::get_module( $mod ) ) {
3752
						$titles[] = '<strong>' . preg_replace( '/\s+(?![^<>]++>)/', '&nbsp;', $mod_headers['name'] ) . '</strong>';
3753
					}
3754
				}
3755
				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...
3756
					$this->message .= '<br /><br />' . wp_sprintf( __( 'The following modules have been updated: %l.', 'jetpack' ), $titles );
3757
				}
3758
			}
3759
3760
			$this->message .= Jetpack::jetpack_comment_notice();
3761
			break;
3762
		case 'jetpack-manage':
3763
			$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>';
3764
			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...
3765
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
3766
			}
3767
			break;
3768
		case 'module_activated' :
3769
			if ( $module = Jetpack::get_module( Jetpack::state( 'module' ) ) ) {
3770
				$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'] );
3771
				$this->stat( 'module-activated', Jetpack::state( 'module' ) );
3772
			}
3773
			break;
3774
3775
		case 'module_deactivated' :
3776
			$modules = Jetpack::state( 'module' );
3777
			if ( ! $modules ) {
3778
				break;
3779
			}
3780
3781
			$module_names = array();
3782
			foreach ( explode( ',', $modules ) as $module_slug ) {
3783
				$module = Jetpack::get_module( $module_slug );
3784
				if ( $module ) {
3785
					$module_names[] = $module['name'];
3786
				}
3787
3788
				$this->stat( 'module-deactivated', $module_slug );
3789
			}
3790
3791
			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...
3792
				break;
3793
			}
3794
3795
			$this->message = wp_sprintf(
3796
				_nx(
3797
					'<strong>%l Deactivated!</strong> You can activate it again at any time using the activate link next to each module.',
3798
					'<strong>%l Deactivated!</strong> You can activate them again at any time using the activate links next to each module.',
3799
					count( $module_names ),
3800
					'%l = list of Jetpack module/feature names',
3801
					'jetpack'
3802
				),
3803
				$module_names
3804
			);
3805
			break;
3806
3807
		case 'module_configured' :
3808
			$this->message = __( '<strong>Module settings were saved.</strong> ', 'jetpack' );
3809
			break;
3810
3811
		case 'already_authorized' :
3812
			$this->message = __( '<strong>Your Jetpack is already connected.</strong> ', 'jetpack' );
3813
			break;
3814
3815
		case 'authorized' :
3816
			$this->message  = __( '<strong>You&#8217;re fueled up and ready to go, Jetpack is now active.</strong> ', 'jetpack' );
3817
			$this->message .= Jetpack::jetpack_comment_notice();
3818
			break;
3819
3820
		case 'linked' :
3821
			$this->message  = __( '<strong>You&#8217;re fueled up and ready to go.</strong> ', 'jetpack' );
3822
			$this->message .= Jetpack::jetpack_comment_notice();
3823
			break;
3824
3825
		case 'unlinked' :
3826
			$user = wp_get_current_user();
3827
			$this->message = sprintf( __( '<strong>You have unlinked your account (%s) from WordPress.com.</strong>', 'jetpack' ), $user->user_login );
3828
			break;
3829
3830
		case 'switch_master' :
3831
			global $current_user;
3832
			$is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
3833
			$master_userdata = get_userdata( Jetpack_Options::get_option( 'master_user' ) );
3834
			if ( $is_master_user ) {
3835
				$this->message = __( 'You have successfully set yourself as Jetpack’s primary user.', 'jetpack' );
3836
			} else {
3837
				$this->message = sprintf( _x( 'You have successfully set %s as Jetpack’s primary user.', '%s is a username', 'jetpack' ), $master_userdata->user_login );
3838
			}
3839
			break;
3840
		}
3841
3842
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
3843
3844
		if ( ! empty( $deactivated_plugins ) ) {
3845
			$deactivated_plugins = explode( ',', $deactivated_plugins );
3846
			$deactivated_titles  = array();
3847
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
3848
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
3849
					continue;
3850
				}
3851
3852
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
3853
			}
3854
3855
			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...
3856
				if ( $this->message ) {
3857
					$this->message .= "<br /><br />\n";
3858
				}
3859
3860
				$this->message .= wp_sprintf(
3861
					_n(
3862
						'Jetpack contains the most recent version of the old %l plugin.',
3863
						'Jetpack contains the most recent versions of the old %l plugins.',
3864
						count( $deactivated_titles ),
3865
						'jetpack'
3866
					),
3867
					$deactivated_titles
3868
				);
3869
3870
				$this->message .= "<br />\n";
3871
3872
				$this->message .= _n(
3873
					'The old version has been deactivated and can be removed from your site.',
3874
					'The old versions have been deactivated and can be removed from your site.',
3875
					count( $deactivated_titles ),
3876
					'jetpack'
3877
				);
3878
			}
3879
		}
3880
3881
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
3882
3883
		if ( $this->message || $this->error || $this->privacy_checks || $this->can_display_jetpack_manage_notice() ) {
3884
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
3885
		}
3886
3887 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
3888
			/**
3889
			 * Fires when a module configuration page is loaded.
3890
			 * The dynamic part of the hook is the configure parameter from the URL.
3891
			 *
3892
			 * @since 1.1.0
3893
			 */
3894
			do_action( 'jetpack_module_configuration_load_' . $_GET['configure'] );
3895
		}
3896
3897
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
3898
	}
3899
3900
	function admin_notices() {
3901
3902
		if ( $this->error ) {
3903
?>
3904
<div id="message" class="jetpack-message jetpack-err">
3905
	<div class="squeezer">
3906
		<h2><?php echo wp_kses( $this->error, array( 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
3907
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
3908
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
3909
<?php	endif; ?>
3910
	</div>
3911
</div>
3912
<?php
3913
		}
3914
3915
		if ( $this->message ) {
3916
?>
3917
<div id="message" class="jetpack-message">
3918
	<div class="squeezer">
3919
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
3920
	</div>
3921
</div>
3922
<?php
3923
		}
3924
3925
		if ( $this->privacy_checks ) :
3926
			$module_names = $module_slugs = array();
3927
3928
			$privacy_checks = explode( ',', $this->privacy_checks );
3929
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
3930
			foreach ( $privacy_checks as $module_slug ) {
3931
				$module = Jetpack::get_module( $module_slug );
3932
				if ( ! $module ) {
3933
					continue;
3934
				}
3935
3936
				$module_slugs[] = $module_slug;
3937
				$module_names[] = "<strong>{$module['name']}</strong>";
3938
			}
3939
3940
			$module_slugs = join( ',', $module_slugs );
3941
?>
3942
<div id="message" class="jetpack-message jetpack-err">
3943
	<div class="squeezer">
3944
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
3945
		<p><?php
3946
			echo wp_kses(
3947
				wptexturize(
3948
					wp_sprintf(
3949
						_nx(
3950
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
3951
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
3952
							count( $privacy_checks ),
3953
							'%l = list of Jetpack module/feature names',
3954
							'jetpack'
3955
						),
3956
						$module_names
3957
					)
3958
				),
3959
				array( 'strong' => true )
3960
			);
3961
3962
			echo "\n<br />\n";
3963
3964
			echo wp_kses(
3965
				sprintf(
3966
					_nx(
3967
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
3968
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
3969
						count( $privacy_checks ),
3970
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
3971
						'jetpack'
3972
					),
3973
					wp_nonce_url(
3974
						Jetpack::admin_url(
3975
							array(
3976
								'page'   => 'jetpack',
3977
								'action' => 'deactivate',
3978
								'module' => urlencode( $module_slugs ),
3979
							)
3980
						),
3981
						"jetpack_deactivate-$module_slugs"
3982
					),
3983
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
3984
				),
3985
				array( 'a' => array( 'href' => true, 'title' => true ) )
3986
			);
3987
		?></p>
3988
	</div>
3989
</div>
3990
<?php endif;
3991
	// only display the notice if the other stuff is not there
3992
	if( $this->can_display_jetpack_manage_notice() && !  $this->error && ! $this->message && ! $this->privacy_checks ) {
3993
		if( isset( $_GET['page'] ) && 'jetpack' != $_GET['page'] )
3994
			$this->opt_in_jetpack_manage_notice();
3995
		}
3996
	}
3997
3998
	/**
3999
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4000
	 */
4001
	function stat( $group, $detail ) {
4002
		if ( ! isset( $this->stats[ $group ] ) )
4003
			$this->stats[ $group ] = array();
4004
		$this->stats[ $group ][] = $detail;
4005
	}
4006
4007
	/**
4008
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4009
	 */
4010
	function do_stats( $method = '' ) {
4011
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4012
			foreach ( $this->stats as $group => $stats ) {
4013
				if ( is_array( $stats ) && count( $stats ) ) {
4014
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4015
					if ( 'server_side' === $method ) {
4016
						self::do_server_side_stat( $args );
4017
					} else {
4018
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4019
					}
4020
				}
4021
				unset( $this->stats[ $group ] );
4022
			}
4023
		}
4024
	}
4025
4026
	/**
4027
	 * Runs stats code for a one-off, server-side.
4028
	 *
4029
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4030
	 *
4031
	 * @return bool If it worked.
4032
	 */
4033
	static function do_server_side_stat( $args ) {
4034
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4035
		if ( is_wp_error( $response ) )
4036
			return false;
4037
4038
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
4039
			return false;
4040
4041
		return true;
4042
	}
4043
4044
	/**
4045
	 * Builds the stats url.
4046
	 *
4047
	 * @param $args array|string The arguments to append to the URL.
4048
	 *
4049
	 * @return string The URL to be pinged.
4050
	 */
4051
	static function build_stats_url( $args ) {
4052
		$defaults = array(
4053
			'v'    => 'wpcom2',
4054
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4055
		);
4056
		$args     = wp_parse_args( $args, $defaults );
4057
		/**
4058
		 * Filter the URL used as the Stats tracking pixel.
4059
		 *
4060
		 * @since 2.3.2
4061
		 *
4062
		 * @param string $url Base URL used as the Stats tracking pixel.
4063
		 */
4064
		$base_url = apply_filters(
4065
			'jetpack_stats_base_url',
4066
			set_url_scheme( 'http://pixel.wp.com/g.gif' )
4067
		);
4068
		$url      = add_query_arg( $args, $base_url );
4069
		return $url;
4070
	}
4071
4072
	static function translate_current_user_to_role() {
4073
		foreach ( self::$capability_translations as $role => $cap ) {
4074
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
4075
				return $role;
4076
			}
4077
		}
4078
4079
		return false;
4080
	}
4081
4082
	static function translate_role_to_cap( $role ) {
4083
		if ( ! isset( self::$capability_translations[$role] ) ) {
4084
			return false;
4085
		}
4086
4087
		return self::$capability_translations[$role];
4088
	}
4089
4090
	static function sign_role( $role ) {
4091
		if ( ! $user_id = (int) get_current_user_id() ) {
4092
			return false;
4093
		}
4094
4095
		$token = Jetpack_Data::get_access_token();
4096
		if ( ! $token || is_wp_error( $token ) ) {
4097
			return false;
4098
		}
4099
4100
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4101
	}
4102
4103
	function build_connect_url( $raw = false, $redirect = false ) {
4104
		if ( ! Jetpack_Options::get_option( 'blog_token' ) || ! Jetpack_Options::get_option( 'id' ) ) {
4105
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4106
			if( is_network_admin() ) {
4107
			    $url = add_query_arg( 'is_multisite', network_admin_url(
4108
			    'admin.php?page=jetpack-settings' ), $url );
4109
			}
4110
		} else {
4111
			$role = self::translate_current_user_to_role();
4112
			$signed_role = $this->sign_role( $role );
4113
4114
			$user = wp_get_current_user();
4115
4116
			$redirect = $redirect ? esc_url_raw( $redirect ) : '';
4117
4118
			if( isset( $_REQUEST['is_multisite'] ) ) {
4119
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4120
			}
4121
4122
			$args = urlencode_deep(
4123
				array(
4124
					'response_type' => 'code',
4125
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4126
					'redirect_uri'  => add_query_arg(
4127
						array(
4128
							'action'   => 'authorize',
4129
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4130
							'redirect' => $redirect ? urlencode( $redirect ) : false,
4131
						),
4132
						menu_page_url( 'jetpack', false )
4133
					),
4134
					'state'         => $user->ID,
4135
					'scope'         => $signed_role,
4136
					'user_email'    => $user->user_email,
4137
					'user_login'    => $user->user_login,
4138
					'is_active'     => Jetpack::is_active(),
4139
					'jp_version'    => JETPACK__VERSION,
4140
				)
4141
			);
4142
4143
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4144
		}
4145
4146
		return $raw ? $url : esc_url( $url );
4147
	}
4148
4149
	function build_reconnect_url( $raw = false ) {
4150
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4151
		return $raw ? $url : esc_url( $url );
4152
	}
4153
4154
	public static function admin_url( $args = null ) {
4155
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4156
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4157
		return $url;
4158
	}
4159
4160
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4161
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4162
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4163
	}
4164
4165
	function dismiss_jetpack_notice() {
4166
4167
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4168
			return;
4169
		}
4170
4171
		switch( $_GET['jetpack-notice'] ) {
4172
			case 'dismiss':
4173
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4174
4175
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4176
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4177
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4178
				}
4179
				break;
4180 View Code Duplication
			case 'jetpack-manage-opt-out':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
4181
4182
				if ( check_admin_referer( 'jetpack_manage_banner_opt_out' ) ) {
4183
					// Don't show the banner again
4184
4185
					Jetpack_Options::update_option( 'dismissed_manage_banner', true );
4186
					// redirect back to the page that had the notice
4187
					if ( wp_get_referer() ) {
4188
						wp_safe_redirect( wp_get_referer() );
4189
					} else {
4190
						// Take me to Jetpack
4191
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4192
					}
4193
				}
4194
				break;
4195 View Code Duplication
			case 'jetpack-protect-multisite-opt-out':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
4196
4197
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4198
					// Don't show the banner again
4199
4200
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4201
					// redirect back to the page that had the notice
4202
					if ( wp_get_referer() ) {
4203
						wp_safe_redirect( wp_get_referer() );
4204
					} else {
4205
						// Take me to Jetpack
4206
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4207
					}
4208
				}
4209
				break;
4210
			case 'jetpack-manage-opt-in':
4211
				if ( check_admin_referer( 'jetpack_manage_banner_opt_in' ) ) {
4212
					// This makes sure that we are redirect to jetpack home so that we can see the Success Message.
4213
4214
					$redirection_url = Jetpack::admin_url();
4215
					remove_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4216
4217
					// Don't redirect form the Jetpack Setting Page
4218
					$referer_parsed = parse_url ( wp_get_referer() );
4219
					// check that we do have a wp_get_referer and the query paramater is set orderwise go to the Jetpack Home
4220
					if ( isset( $referer_parsed['query'] ) && false !== strpos( $referer_parsed['query'], 'page=jetpack_modules' ) ) {
4221
						// Take the user to Jetpack home except when on the setting page
4222
						$redirection_url = wp_get_referer();
4223
						add_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4224
					}
4225
					// Also update the JSON API FULL MANAGEMENT Option
4226
					Jetpack::activate_module( 'manage', false, false );
4227
4228
					// Special Message when option in.
4229
					Jetpack::state( 'optin-manage', 'true' );
4230
					// Activate the Module if not activated already
4231
4232
					// Redirect properly
4233
					wp_safe_redirect( $redirection_url );
4234
4235
				}
4236
				break;
4237
		}
4238
	}
4239
4240
	function debugger_page() {
4241
		nocache_headers();
4242
		if ( ! current_user_can( 'manage_options' ) ) {
4243
			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...
4244
		}
4245
		Jetpack_Debugger::jetpack_debug_display_handler();
4246
		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...
4247
	}
4248
4249
	public static function admin_screen_configure_module( $module_id ) {
4250
4251
		// User that doesn't have 'jetpack_configure_modules' will never end up here since Jetpack Landing Page woun't let them.
4252
		if ( ! in_array( $module_id, Jetpack::get_active_modules() ) && current_user_can( 'manage_options' ) ) {
4253
			if ( has_action( 'display_activate_module_setting_' . $module_id ) ) {
4254
				/**
4255
				 * Fires to diplay a custom module activation screen.
4256
				 *
4257
				 * To add a module actionation screen use Jetpack::module_configuration_activation_screen method.
4258
				 * Example: Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
4259
				 *
4260
				 * @module manage
4261
				 *
4262
				 * @since 3.8.0
4263
				 *
4264
				 * @param int $module_id Module ID.
4265
				 */
4266
				do_action( 'display_activate_module_setting_' . $module_id );
4267
			} else {
4268
				self::display_activate_module_link( $module_id );
4269
			}
4270
4271
			return false;
4272
		} ?>
4273
4274
		<div id="jp-settings-screen" style="position: relative">
4275
			<h3>
4276
			<?php
4277
				$module = Jetpack::get_module( $module_id );
4278
				echo '<a href="' . Jetpack::admin_url( 'page=jetpack_modules' ) . '">' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</a> &rarr; ';
4279
				printf( __( 'Configure %s', 'jetpack' ), $module['name'] );
4280
			?>
4281
			</h3>
4282
			<?php
4283
				/**
4284
				 * Fires within the displayed message when a feature configuation is updated.
4285
				 *
4286
				 * @since 3.4.0
4287
				 *
4288
				 * @param int $module_id Module ID.
4289
				 */
4290
				do_action( 'jetpack_notices_update_settings', $module_id );
4291
				/**
4292
				 * Fires when a feature configuation screen is loaded.
4293
				 * The dynamic part of the hook, $module_id, is the module ID.
4294
				 *
4295
				 * @since 1.1.0
4296
				 */
4297
				do_action( 'jetpack_module_configuration_screen_' . $module_id );
4298
			?>
4299
		</div><?php
4300
	}
4301
4302
	/**
4303
	 * Display link to activate the module to see the settings screen.
4304
	 * @param  string $module_id
4305
	 * @return null
4306
	 */
4307
	public static function display_activate_module_link( $module_id ) {
4308
4309
		$info =  Jetpack::get_module( $module_id );
4310
		$extra = '';
4311
		$activate_url = wp_nonce_url(
4312
				Jetpack::admin_url(
4313
					array(
4314
						'page'   => 'jetpack',
4315
						'action' => 'activate',
4316
						'module' => $module_id,
4317
					)
4318
				),
4319
				"jetpack_activate-$module_id"
4320
			);
4321
4322
		?>
4323
4324
		<div class="wrap configure-module">
4325
			<div id="jp-settings-screen">
4326
				<?php
4327
				if ( $module_id == 'json-api' ) {
4328
4329
					$info['name'] = esc_html__( 'Activate Site Management and JSON API', 'jetpack' );
4330
4331
					$activate_url = Jetpack::init()->opt_in_jetpack_manage_url();
4332
4333
					$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' );
4334
4335
					// $extra = __( 'To use Site Management, you need to first activate JSON API to allow remote management of your site. ', 'jetpack' );
4336
				} ?>
4337
4338
				<h3><?php echo esc_html( $info['name'] ); ?></h3>
4339
				<div class="narrow">
4340
					<p><?php echo  $info['description']; ?></p>
4341
					<?php if( $extra ) { ?>
4342
					<p><?php echo esc_html( $extra ); ?></p>
4343
					<?php } ?>
4344
					<p>
4345
						<?php
4346
						if( wp_get_referer() ) {
4347
							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() );
4348
						} else {
4349
							printf( __( '<a class="button-primary" href="%s">Activate Now</a>', 'jetpack' ) , $activate_url  );
4350
						} ?>
4351
					</p>
4352
				</div>
4353
4354
			</div>
4355
		</div>
4356
4357
		<?php
4358
	}
4359
4360
	public static function sort_modules( $a, $b ) {
4361
		if ( $a['sort'] == $b['sort'] )
4362
			return 0;
4363
4364
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4365
	}
4366
4367
/* Client API */
4368
4369
	/**
4370
	 * Returns the requested Jetpack API URL
4371
	 *
4372
	 * @return string
4373
	 */
4374
	public static function api_url( $relative_url ) {
4375
		return trailingslashit( JETPACK__API_BASE . $relative_url  ) . JETPACK__API_VERSION . '/';
4376
	}
4377
4378
	/**
4379
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
4380
	 */
4381
	public static function fix_url_for_bad_hosts( $url ) {
4382
		if ( 0 !== strpos( $url, 'https://' ) ) {
4383
			return $url;
4384
		}
4385
4386
		switch ( JETPACK_CLIENT__HTTPS ) {
4387
			case 'ALWAYS' :
4388
				return $url;
4389
			case 'NEVER' :
4390
				return set_url_scheme( $url, 'http' );
4391
			// default : case 'AUTO' :
4392
		}
4393
4394
		// Yay! Your host is good!
4395
		if ( self::permit_ssl() && wp_http_supports( array( 'ssl' => true ) ) ) {
4396
			return $url;
4397
		}
4398
4399
		// Boo! Your host is bad and makes Jetpack cry!
4400
		return set_url_scheme( $url, 'http' );
4401
	}
4402
4403
	/**
4404
	 * Checks to see if the URL is using SSL to connect with Jetpack
4405
	 *
4406
	 * @since 2.3.3
4407
	 * @return boolean
4408
	 */
4409
	public static function permit_ssl( $force_recheck = false ) {
4410
		// Do some fancy tests to see if ssl is being supported
4411
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
4412
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4413
				$ssl = 0;
4414
			} else {
4415
				switch ( JETPACK_CLIENT__HTTPS ) {
4416
					case 'NEVER':
4417
						$ssl = 0;
4418
						break;
4419
					case 'ALWAYS':
4420
					case 'AUTO':
4421
					default:
4422
						$ssl = 1;
4423
						break;
4424
				}
4425
4426
				// If it's not 'NEVER', test to see
4427
				if ( $ssl ) {
4428
					$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4429
					if ( is_wp_error( $response ) || ( 'OK' !== wp_remote_retrieve_body( $response ) ) ) {
4430
						$ssl = 0;
4431
					}
4432
				}
4433
			}
4434
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4435
		}
4436
4437
		return (bool) $ssl;
4438
	}
4439
4440
	/*
4441
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'ALWAYS' but SSL isn't working.
4442
	 */
4443
	public function alert_required_ssl_fail() {
4444
		if ( ! current_user_can( 'manage_options' ) )
4445
			return;
4446
		?>
4447
4448
		<div id="message" class="error jetpack-message jp-identity-crisis">
4449
			<div class="jp-banner__content">
4450
				<h2><?php _e( 'Something is being cranky!', 'jetpack' ); ?></h2>
4451
				<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>
4452
			</div>
4453
		</div>
4454
4455
		<?php
4456
	}
4457
4458
	/**
4459
	 * Returns the Jetpack XML-RPC API
4460
	 *
4461
	 * @return string
4462
	 */
4463
	public static function xmlrpc_api_url() {
4464
		$base = preg_replace( '#(https?://[^?/]+)(/?.*)?$#', '\\1', JETPACK__API_BASE );
4465
		return untrailingslashit( $base ) . '/xmlrpc.php';
4466
	}
4467
4468
	/**
4469
	 * Creates two secret tokens and the end of life timestamp for them.
4470
	 *
4471
	 * Note these tokens are unique per call, NOT static per site for connecting.
4472
	 *
4473
	 * @since 2.6
4474
	 * @return array
4475
	 */
4476
	public function generate_secrets() {
4477
	    $secrets = array(
4478
		wp_generate_password( 32, false ), // secret_1
4479
		wp_generate_password( 32, false ), // secret_2
4480
		( time() + 600 ), // eol ( End of Life )
4481
	    );
4482
4483
	    return $secrets;
4484
	}
4485
4486
	/**
4487
	 * Builds the timeout limit for queries talking with the wpcom servers.
4488
	 *
4489
	 * Based on local php max_execution_time in php.ini
4490
	 *
4491
	 * @since 2.6
4492
	 * @return int
4493
	 **/
4494
	public function get_remote_query_timeout_limit() {
4495
	    $timeout = (int) ini_get( 'max_execution_time' );
4496
	    if ( ! $timeout ) // Ensure exec time set in php.ini
4497
		$timeout = 30;
4498
	    return intval( $timeout / 2 );
4499
	}
4500
4501
4502
	/**
4503
	 * Takes the response from the Jetpack register new site endpoint and
4504
	 * verifies it worked properly.
4505
	 *
4506
	 * @since 2.6
4507
	 * @return true or Jetpack_Error
4508
	 **/
4509
	public function validate_remote_register_response( $response ) {
4510
	    	if ( is_wp_error( $response ) ) {
4511
			return new Jetpack_Error( 'register_http_request_failed', $response->get_error_message() );
4512
		}
4513
4514
		$code   = wp_remote_retrieve_response_code( $response );
4515
		$entity = wp_remote_retrieve_body( $response );
4516
		if ( $entity )
4517
			$json = json_decode( $entity );
4518
		else
4519
			$json = false;
4520
4521
		$code_type = intval( $code / 100 );
4522
		if ( 5 == $code_type ) {
4523
			return new Jetpack_Error( 'wpcom_5??', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4524
		} elseif ( 408 == $code ) {
4525
			return new Jetpack_Error( 'wpcom_408', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4526
		} elseif ( ! empty( $json->error ) ) {
4527
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
4528
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
4529
		} elseif ( 200 != $code ) {
4530
			return new Jetpack_Error( 'wpcom_bad_response', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
4531
		}
4532
4533
		// Jetpack ID error block
4534
		if ( empty( $json->jetpack_id ) ) {
4535
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack' ), $entity ), $entity );
4536
		} elseif ( ! is_scalar( $json->jetpack_id ) ) {
4537
			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 );
4538
		} elseif ( preg_match( '/[^0-9]/', $json->jetpack_id ) ) {
4539
			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 );
4540
		}
4541
4542
	    return true;
4543
	}
4544
	/**
4545
	 * @return bool|WP_Error
4546
	 */
4547
	public static function register() {
4548
		add_action( 'pre_update_jetpack_option_register', array( 'Jetpack_Options', 'delete_option' ) );
4549
		$secrets = Jetpack::init()->generate_secrets();
4550
4551
		Jetpack_Options::update_option( 'register', $secrets[0] . ':' . $secrets[1] . ':' . $secrets[2] );
4552
4553
		@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...
4554
		if ( empty( $secret_1 ) || empty( $secret_2 ) || empty( $secret_eol ) || $secret_eol < time() ) {
4555
			return new Jetpack_Error( 'missing_secrets' );
4556
		}
4557
4558
		$timeout = Jetpack::init()->get_remote_query_timeout_limit();
4559
4560
		$gmt_offset = get_option( 'gmt_offset' );
4561
		if ( ! $gmt_offset ) {
4562
			$gmt_offset = 0;
4563
		}
4564
4565
		$stats_options = get_option( 'stats_options' );
4566
		$stats_id = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
4567
4568
		$args = array(
4569
			'method'  => 'POST',
4570
			'body'    => array(
4571
				'siteurl'         => site_url(),
4572
				'home'            => home_url(),
4573
				'gmt_offset'      => $gmt_offset,
4574
				'timezone_string' => (string) get_option( 'timezone_string' ),
4575
				'site_name'       => (string) get_option( 'blogname' ),
4576
				'secret_1'        => $secret_1,
4577
				'secret_2'        => $secret_2,
4578
				'site_lang'       => get_locale(),
4579
				'timeout'         => $timeout,
4580
				'stats_id'        => $stats_id,
4581
			),
4582
			'headers' => array(
4583
				'Accept' => 'application/json',
4584
			),
4585
			'timeout' => $timeout,
4586
		);
4587
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'register' ) ), $args, true );
4588
4589
4590
		// Make sure the response is valid and does not contain any Jetpack errors
4591
		$valid_response = Jetpack::init()->validate_remote_register_response( $response );
4592
		if( is_wp_error( $valid_response ) || !$valid_response ) {
4593
		    return $valid_response;
4594
		}
4595
4596
		// Grab the response values to work with
4597
		$code   = wp_remote_retrieve_response_code( $response );
4598
		$entity = wp_remote_retrieve_body( $response );
4599
4600
		if ( $entity )
4601
			$json = json_decode( $entity );
4602
		else
4603
			$json = false;
4604
4605 View Code Duplication
		if ( empty( $json->jetpack_secret ) || ! is_string( $json->jetpack_secret ) )
4606
			return new Jetpack_Error( 'jetpack_secret', '', $code );
4607
4608
		if ( isset( $json->jetpack_public ) ) {
4609
			$jetpack_public = (int) $json->jetpack_public;
4610
		} else {
4611
			$jetpack_public = false;
4612
		}
4613
4614
		Jetpack_Options::update_options(
4615
			array(
4616
				'id'         => (int)    $json->jetpack_id,
4617
				'blog_token' => (string) $json->jetpack_secret,
4618
				'public'     => $jetpack_public,
4619
			)
4620
		);
4621
4622
		/**
4623
		 * Fires when a site is registered on WordPress.com.
4624
		 *
4625
		 * @since 3.7.0
4626
		 *
4627
		 * @param int $json->jetpack_id Jetpack Blog ID.
4628
		 * @param string $json->jetpack_secret Jetpack Blog Token.
4629
		 * @param int|bool $jetpack_public Is the site public.
4630
		 */
4631
		do_action( 'jetpack_site_registered', $json->jetpack_id, $json->jetpack_secret, $jetpack_public );
4632
4633
		// Initialize Jump Start for the first and only time.
4634
		if ( ! Jetpack_Options::get_option( 'jumpstart' ) ) {
4635
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
4636
4637
			$jetpack = Jetpack::init();
4638
4639
			$jetpack->stat( 'jumpstart', 'unique-views' );
4640
			$jetpack->do_stats( 'server_side' );
4641
		};
4642
4643
		return true;
4644
	}
4645
4646
	/**
4647
	 * If the db version is showing something other that what we've got now, bump it to current.
4648
	 *
4649
	 * @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...
4650
	 */
4651
	public static function maybe_set_version_option() {
4652
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
4653
		if ( JETPACK__VERSION != $version ) {
4654
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
4655
			return true;
4656
		}
4657
		return false;
4658
	}
4659
4660
/* Client Server API */
4661
4662
	/**
4663
	 * Loads the Jetpack XML-RPC client
4664
	 */
4665
	public static function load_xml_rpc_client() {
4666
		require_once ABSPATH . WPINC . '/class-IXR.php';
4667
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-ixr-client.php';
4668
	}
4669
4670
	function verify_xml_rpc_signature() {
4671
		if ( $this->xmlrpc_verification ) {
4672
			return $this->xmlrpc_verification;
4673
		}
4674
4675
		// It's not for us
4676
		if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
4677
			return false;
4678
		}
4679
4680
		@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...
4681
		if (
4682
			empty( $token_key )
4683
		||
4684
			empty( $version ) || strval( JETPACK__API_VERSION ) !== $version
4685
		) {
4686
			return false;
4687
		}
4688
4689
		if ( '0' === $user_id ) {
4690
			$token_type = 'blog';
4691
			$user_id = 0;
4692
		} else {
4693
			$token_type = 'user';
4694
			if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
4695
				return false;
4696
			}
4697
			$user_id = (int) $user_id;
4698
4699
			$user = new WP_User( $user_id );
4700
			if ( ! $user || ! $user->exists() ) {
4701
				return false;
4702
			}
4703
		}
4704
4705
		$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...
4706
		if ( ! $token ) {
4707
			return false;
4708
		}
4709
4710
		$token_check = "$token_key.";
4711
		if ( ! hash_equals( substr( $token->secret, 0, strlen( $token_check ) ), $token_check ) ) {
4712
			return false;
4713
		}
4714
4715
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
4716
4717
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
4718
		if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
4719
			$post_data   = $_POST;
4720
			$file_hashes = array();
4721
			foreach ( $post_data as $post_data_key => $post_data_value ) {
4722
				if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
4723
					continue;
4724
				}
4725
				$post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
4726
				$file_hashes[$post_data_key] = $post_data_value;
4727
			}
4728
4729
			foreach ( $file_hashes as $post_data_key => $post_data_value ) {
4730
				unset( $post_data["_jetpack_file_hmac_{$post_data_key}"] );
4731
				$post_data[$post_data_key] = $post_data_value;
4732
			}
4733
4734
			ksort( $post_data );
4735
4736
			$body = http_build_query( stripslashes_deep( $post_data ) );
4737
		} elseif ( is_null( $this->HTTP_RAW_POST_DATA ) ) {
4738
			$body = file_get_contents( 'php://input' );
4739
		} else {
4740
			$body = null;
4741
		}
4742
		$signature = $jetpack_signature->sign_current_request(
4743
			array( 'body' => is_null( $body ) ? $this->HTTP_RAW_POST_DATA : $body, )
4744
		);
4745
4746
		if ( ! $signature ) {
4747
			return false;
4748
		} else if ( is_wp_error( $signature ) ) {
4749
			return $signature;
4750
		} else if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
4751
			return false;
4752
		}
4753
4754
		$timestamp = (int) $_GET['timestamp'];
4755
		$nonce     = stripslashes( (string) $_GET['nonce'] );
4756
4757
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
4758
			return false;
4759
		}
4760
4761
		$this->xmlrpc_verification = array(
4762
			'type'    => $token_type,
4763
			'user_id' => $token->external_user_id,
4764
		);
4765
4766
		return $this->xmlrpc_verification;
4767
	}
4768
4769
	/**
4770
	 * Authenticates XML-RPC and other requests from the Jetpack Server
4771
	 */
4772
	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...
4773
		if ( is_a( $user, 'WP_User' ) ) {
4774
			return $user;
4775
		}
4776
4777
		$token_details = $this->verify_xml_rpc_signature();
4778
4779
		if ( ! $token_details || is_wp_error( $token_details ) ) {
4780
			return $user;
4781
		}
4782
4783
		if ( 'user' !== $token_details['type'] ) {
4784
			return $user;
4785
		}
4786
4787
		if ( ! $token_details['user_id'] ) {
4788
			return $user;
4789
		}
4790
4791
		nocache_headers();
4792
4793
		return new WP_User( $token_details['user_id'] );
4794
	}
4795
4796
	function add_nonce( $timestamp, $nonce ) {
4797
		global $wpdb;
4798
		static $nonces_used_this_request = array();
4799
4800
		if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) {
4801
			return $nonces_used_this_request["$timestamp:$nonce"];
4802
		}
4803
4804
		// This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce
4805
		$timestamp = (int) $timestamp;
4806
		$nonce     = esc_sql( $nonce );
4807
4808
		// Raw query so we can avoid races: add_option will also update
4809
		$show_errors = $wpdb->show_errors( false );
4810
4811
		$old_nonce = $wpdb->get_row(
4812
			$wpdb->prepare( "SELECT * FROM `$wpdb->options` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
4813
		);
4814
4815
		if ( is_null( $old_nonce ) ) {
4816
			$return = $wpdb->query(
4817
				$wpdb->prepare(
4818
					"INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
4819
					"jetpack_nonce_{$timestamp}_{$nonce}",
4820
					time(),
4821
					'no'
4822
				)
4823
			);
4824
		} else {
4825
			$return = false;
4826
		}
4827
4828
		$wpdb->show_errors( $show_errors );
4829
4830
		$nonces_used_this_request["$timestamp:$nonce"] = $return;
4831
4832
		return $return;
4833
	}
4834
4835
	/**
4836
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
4837
	 * Capture it here so we can verify the signature later.
4838
	 */
4839
	function xmlrpc_methods( $methods ) {
4840
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
4841
		return $methods;
4842
	}
4843
4844
	function public_xmlrpc_methods( $methods ) {
4845
		if ( array_key_exists( 'wp.getOptions', $methods ) ) {
4846
			$methods['wp.getOptions'] = array( $this, 'jetpack_getOptions' );
4847
		}
4848
		return $methods;
4849
	}
4850
4851
	function jetpack_getOptions( $args ) {
4852
		global $wp_xmlrpc_server;
4853
4854
		$wp_xmlrpc_server->escape( $args );
4855
4856
		$username	= $args[1];
4857
		$password	= $args[2];
4858
4859
		if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
4860
			return $wp_xmlrpc_server->error;
4861
		}
4862
4863
		$options = array();
4864
		$user_data = $this->get_connected_user_data();
4865
		if ( is_array( $user_data ) ) {
4866
			$options['jetpack_user_id'] = array(
4867
				'desc'          => __( 'The WP.com user ID of the connected user', 'jetpack' ),
4868
				'readonly'      => true,
4869
				'value'         => $user_data['ID'],
4870
			);
4871
			$options['jetpack_user_login'] = array(
4872
				'desc'          => __( 'The WP.com username of the connected user', 'jetpack' ),
4873
				'readonly'      => true,
4874
				'value'         => $user_data['login'],
4875
			);
4876
			$options['jetpack_user_email'] = array(
4877
				'desc'          => __( 'The WP.com user email of the connected user', 'jetpack' ),
4878
				'readonly'      => true,
4879
				'value'         => $user_data['email'],
4880
			);
4881
			$options['jetpack_user_site_count'] = array(
4882
				'desc'          => __( 'The number of sites of the connected WP.com user', 'jetpack' ),
4883
				'readonly'      => true,
4884
				'value'         => $user_data['site_count'],
4885
			);
4886
		}
4887
		$wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
4888
		$args = stripslashes_deep( $args );
4889
		return $wp_xmlrpc_server->wp_getOptions( $args );
4890
	}
4891
4892
	function xmlrpc_options( $options ) {
4893
		$jetpack_client_id = false;
4894
		if ( self::is_active() ) {
4895
			$jetpack_client_id = Jetpack_Options::get_option( 'id' );
4896
		}
4897
		$options['jetpack_version'] = array(
4898
				'desc'          => __( 'Jetpack Plugin Version', 'jetpack' ),
4899
				'readonly'      => true,
4900
				'value'         => JETPACK__VERSION,
4901
		);
4902
4903
		$options['jetpack_client_id'] = array(
4904
				'desc'          => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack' ),
4905
				'readonly'      => true,
4906
				'value'         => $jetpack_client_id,
4907
		);
4908
		return $options;
4909
	}
4910
4911
	public static function clean_nonces( $all = false ) {
4912
		global $wpdb;
4913
4914
		$sql = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE %s";
4915
		if ( method_exists ( $wpdb , 'esc_like' ) ) {
4916
			$sql_args = array( $wpdb->esc_like( 'jetpack_nonce_' ) . '%' );
4917
		} else {
4918
			$sql_args = array( like_escape( 'jetpack_nonce_' ) . '%' );
4919
		}
4920
4921
		if ( true !== $all ) {
4922
			$sql .= ' AND CAST( `option_value` AS UNSIGNED ) < %d';
4923
			$sql_args[] = time() - 3600;
4924
		}
4925
4926
		$sql .= ' ORDER BY `option_id` LIMIT 100';
4927
4928
		$sql = $wpdb->prepare( $sql, $sql_args );
4929
4930
		for ( $i = 0; $i < 1000; $i++ ) {
4931
			if ( ! $wpdb->query( $sql ) ) {
4932
				break;
4933
			}
4934
		}
4935
	}
4936
4937
	/**
4938
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
4939
	 * SET: state( $key, $value );
4940
	 * GET: $value = state( $key );
4941
	 *
4942
	 * @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...
4943
	 * @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...
4944
	 * @param bool $restate private
4945
	 */
4946
	public static function state( $key = null, $value = null, $restate = false ) {
4947
		static $state = array();
4948
		static $path, $domain;
4949
		if ( ! isset( $path ) ) {
4950
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
4951
			$admin_url = Jetpack::admin_url();
4952
			$bits      = parse_url( $admin_url );
4953
4954
			if ( is_array( $bits ) ) {
4955
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
4956
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
4957
			} else {
4958
				$path = $domain = null;
4959
			}
4960
		}
4961
4962
		// Extract state from cookies and delete cookies
4963
		if ( isset( $_COOKIE[ 'jetpackState' ] ) && is_array( $_COOKIE[ 'jetpackState' ] ) ) {
4964
			$yum = $_COOKIE[ 'jetpackState' ];
4965
			unset( $_COOKIE[ 'jetpackState' ] );
4966
			foreach ( $yum as $k => $v ) {
4967
				if ( strlen( $v ) )
4968
					$state[ $k ] = $v;
4969
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
4970
			}
4971
		}
4972
4973
		if ( $restate ) {
4974
			foreach ( $state as $k => $v ) {
4975
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
4976
			}
4977
			return;
4978
		}
4979
4980
		// Get a state variable
4981
		if ( isset( $key ) && ! isset( $value ) ) {
4982
			if ( array_key_exists( $key, $state ) )
4983
				return $state[ $key ];
4984
			return null;
4985
		}
4986
4987
		// Set a state variable
4988
		if ( isset ( $key ) && isset( $value ) ) {
4989
			if( is_array( $value ) && isset( $value[0] ) ) {
4990
				$value = $value[0];
4991
			}
4992
			$state[ $key ] = $value;
4993
			setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
4994
		}
4995
	}
4996
4997
	public static function restate() {
4998
		Jetpack::state( null, null, true );
4999
	}
5000
5001
	public static function check_privacy( $file ) {
5002
		static $is_site_publicly_accessible = null;
5003
5004
		if ( is_null( $is_site_publicly_accessible ) ) {
5005
			$is_site_publicly_accessible = false;
5006
5007
			Jetpack::load_xml_rpc_client();
5008
			$rpc = new Jetpack_IXR_Client();
5009
5010
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5011
			if ( $success ) {
5012
				$response = $rpc->getResponse();
5013
				if ( $response ) {
5014
					$is_site_publicly_accessible = true;
5015
				}
5016
			}
5017
5018
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5019
		}
5020
5021
		if ( $is_site_publicly_accessible ) {
5022
			return;
5023
		}
5024
5025
		$module_slug = self::get_module_slug( $file );
5026
5027
		$privacy_checks = Jetpack::state( 'privacy_checks' );
5028
		if ( ! $privacy_checks ) {
5029
			$privacy_checks = $module_slug;
5030
		} else {
5031
			$privacy_checks .= ",$module_slug";
5032
		}
5033
5034
		Jetpack::state( 'privacy_checks', $privacy_checks );
5035
	}
5036
5037
	/**
5038
	 * Helper method for multicall XMLRPC.
5039
	 */
5040
	public static function xmlrpc_async_call() {
5041
		global $blog_id;
5042
		static $clients = array();
5043
5044
		$client_blog_id = is_multisite() ? $blog_id : 0;
5045
5046
		if ( ! isset( $clients[$client_blog_id] ) ) {
5047
			Jetpack::load_xml_rpc_client();
5048
			$clients[$client_blog_id] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER, ) );
5049
			if ( function_exists( 'ignore_user_abort' ) ) {
5050
				ignore_user_abort( true );
5051
			}
5052
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5053
		}
5054
5055
		$args = func_get_args();
5056
5057
		if ( ! empty( $args[0] ) ) {
5058
			call_user_func_array( array( $clients[$client_blog_id], 'addCall' ), $args );
5059
		} elseif ( is_multisite() ) {
5060
			foreach ( $clients as $client_blog_id => $client ) {
5061
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5062
					continue;
5063
				}
5064
5065
				$switch_success = switch_to_blog( $client_blog_id, true );
5066
				if ( ! $switch_success ) {
5067
					continue;
5068
				}
5069
5070
				flush();
5071
				$client->query();
5072
5073
				restore_current_blog();
5074
			}
5075
		} else {
5076
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5077
				flush();
5078
				$clients[0]->query();
5079
			}
5080
		}
5081
	}
5082
5083
	public static function staticize_subdomain( $url ) {
5084
5085
		// Extract hostname from URL
5086
		$host = parse_url( $url, PHP_URL_HOST );
5087
5088
		// Explode hostname on '.'
5089
		$exploded_host = explode( '.', $host );
5090
5091
		// Retrieve the name and TLD
5092
		if ( count( $exploded_host ) > 1 ) {
5093
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
5094
			$tld = $exploded_host[ count( $exploded_host ) - 1 ];
5095
			// Rebuild domain excluding subdomains
5096
			$domain = $name . '.' . $tld;
5097
		} else {
5098
			$domain = $host;
5099
		}
5100
		// Array of Automattic domains
5101
		$domain_whitelist = array( 'wordpress.com', 'wp.com' );
5102
5103
		// Return $url if not an Automattic domain
5104
		if ( ! in_array( $domain, $domain_whitelist ) ) {
5105
			return $url;
5106
		}
5107
5108
		if ( is_ssl() ) {
5109
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
5110
		}
5111
5112
		srand( crc32( basename( $url ) ) );
5113
		$static_counter = rand( 0, 2 );
5114
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
5115
5116
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
5117
	}
5118
5119
/* JSON API Authorization */
5120
5121
	/**
5122
	 * Handles the login action for Authorizing the JSON API
5123
	 */
5124
	function login_form_json_api_authorization() {
5125
		$this->verify_json_api_authorization_request();
5126
5127
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
5128
5129
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
5130
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5131
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
5132
	}
5133
5134
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
5135
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
5136
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
5137
			return $url;
5138
		}
5139
5140
		$parsed_url = parse_url( $url );
5141
		$url = strtok( $url, '?' );
5142
		$url = "$url?{$_SERVER['QUERY_STRING']}";
5143
		if ( ! empty( $parsed_url['query'] ) )
5144
			$url .= "&{$parsed_url['query']}";
5145
5146
		return $url;
5147
	}
5148
5149
	// Make sure the POSTed request is handled by the same action
5150
	function preserve_action_in_login_form_for_json_api_authorization() {
5151
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
5152
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
5153
	}
5154
5155
	// If someone logs in to approve API access, store the Access Code in usermeta
5156
	function store_json_api_authorization_token( $user_login, $user ) {
5157
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5158
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
5159
		$token = wp_generate_password( 32, false );
5160
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5161
	}
5162
5163
	// Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access
5164
	function allow_wpcom_public_api_domain( $domains ) {
5165
		$domains[] = 'public-api.wordpress.com';
5166
		return $domains;
5167
	}
5168
5169
	// Add the Access Code details to the public-api.wordpress.com redirect
5170
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5171
		return add_query_arg(
5172
			urlencode_deep(
5173
				array(
5174
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5175
					'jetpack-user-id' => (int) $user->ID,
5176
					'jetpack-state'   => $this->json_api_authorization_request['state'],
5177
				)
5178
			),
5179
			$redirect_to
5180
		);
5181
	}
5182
5183
	// Verifies the request by checking the signature
5184
	function verify_json_api_authorization_request() {
5185
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
5186
5187
		$token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
5188
		if ( ! $token || empty( $token->secret ) ) {
5189
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' , 'jetpack' ) );
5190
		}
5191
5192
		$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' );
5193
5194
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5195
5196
		if ( isset( $_POST['jetpack_json_api_original_query'] ) ) {
5197
			$signature = $jetpack_signature->sign_request( $_GET['token'], $_GET['timestamp'], $_GET['nonce'], '', 'GET', $_POST['jetpack_json_api_original_query'], null, true );
5198
		} else {
5199
			$signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) );
5200
		}
5201
5202
		if ( ! $signature ) {
5203
			wp_die( $die_error );
5204
		} else if ( is_wp_error( $signature ) ) {
5205
			wp_die( $die_error );
5206
		} else if ( $signature !== $_GET['signature'] ) {
5207
			if ( is_ssl() ) {
5208
				// 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
5209
				$signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) );
5210
				if ( ! $signature || is_wp_error( $signature ) || $signature !== $_GET['signature'] ) {
5211
					wp_die( $die_error );
5212
				}
5213
			} else {
5214
				wp_die( $die_error );
5215
			}
5216
		}
5217
5218
		$timestamp = (int) $_GET['timestamp'];
5219
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5220
5221
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5222
			// De-nonce the nonce, at least for 5 minutes.
5223
			// 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)
5224
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5225
			if ( $old_nonce_time < time() - 300 ) {
5226
				wp_die( __( 'The authorization process expired.  Please go back and try again.' , 'jetpack' ) );
5227
			}
5228
		}
5229
5230
		$data = json_decode( base64_decode( stripslashes( $_GET['data'] ) ) );
5231
		$data_filters = array(
5232
			'state'        => 'opaque',
5233
			'client_id'    => 'int',
5234
			'client_title' => 'string',
5235
			'client_image' => 'url',
5236
		);
5237
5238
		foreach ( $data_filters as $key => $sanitation ) {
5239
			if ( ! isset( $data->$key ) ) {
5240
				wp_die( $die_error );
5241
			}
5242
5243
			switch ( $sanitation ) {
5244
			case 'int' :
5245
				$this->json_api_authorization_request[$key] = (int) $data->$key;
5246
				break;
5247
			case 'opaque' :
5248
				$this->json_api_authorization_request[$key] = (string) $data->$key;
5249
				break;
5250
			case 'string' :
5251
				$this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() );
5252
				break;
5253
			case 'url' :
5254
				$this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key );
5255
				break;
5256
			}
5257
		}
5258
5259
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5260
			wp_die( $die_error );
5261
		}
5262
	}
5263
5264
	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...
5265
		return '<p class="message">' . sprintf(
5266
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' , 'jetpack' ),
5267
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5268
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5269
	}
5270
5271
	/**
5272
	 * Get $content_width, but with a <s>twist</s> filter.
5273
	 */
5274
	public static function get_content_width() {
5275
		$content_width = isset( $GLOBALS['content_width'] ) ? $GLOBALS['content_width'] : false;
5276
		/**
5277
		 * Filter the Content Width value.
5278
		 *
5279
		 * @since 2.2.3
5280
		 *
5281
		 * @param string $content_width Content Width value.
5282
		 */
5283
		return apply_filters( 'jetpack_content_width', $content_width );
5284
	}
5285
5286
	/**
5287
	 * Centralize the function here until it gets added to core.
5288
	 *
5289
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
5290
	 * @param int $size Size of the avatar image
5291
	 * @param string $default URL to a default image to use if no avatar is available
5292
	 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
5293
	 *
5294
	 * @return array First element is the URL, second is the class.
5295
	 */
5296
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
5297
		// Don't bother adding the __return_true filter if it's already there.
5298
		$has_filter = has_filter( 'pre_option_show_avatars', '__return_true' );
5299
5300
		if ( $force_display && ! $has_filter )
5301
			add_filter( 'pre_option_show_avatars', '__return_true' );
5302
5303
		$avatar = get_avatar( $id_or_email, $size, $default );
5304
5305
		if ( $force_display && ! $has_filter )
5306
			remove_filter( 'pre_option_show_avatars', '__return_true' );
5307
5308
		// If no data, fail out.
5309
		if ( is_wp_error( $avatar ) || ! $avatar )
5310
			return array( null, null );
5311
5312
		// Pull out the URL.  If it's not there, fail out.
5313
		if ( ! preg_match( '/src=["\']([^"\']+)["\']/', $avatar, $url_matches ) )
5314
			return array( null, null );
5315
		$url = wp_specialchars_decode( $url_matches[1], ENT_QUOTES );
5316
5317
		// Pull out the class, but it's not a big deal if it's missing.
5318
		$class = '';
5319
		if ( preg_match( '/class=["\']([^"\']+)["\']/', $avatar, $class_matches ) )
5320
			$class = wp_specialchars_decode( $class_matches[1], ENT_QUOTES );
5321
5322
		return array( $url, $class );
5323
	}
5324
5325
	/**
5326
	 * Pings the WordPress.com Mirror Site for the specified options.
5327
	 *
5328
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
5329
	 *
5330
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
5331
	 */
5332
	public function get_cloud_site_options( $option_names ) {
5333
		$option_names = array_filter( (array) $option_names, 'is_string' );
5334
5335
		Jetpack::load_xml_rpc_client();
5336
		$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
5337
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
5338
		if ( $xml->isError() ) {
5339
			return array(
5340
				'error_code' => $xml->getErrorCode(),
5341
				'error_msg'  => $xml->getErrorMessage(),
5342
			);
5343
		}
5344
		$cloud_site_options = $xml->getResponse();
5345
5346
		return $cloud_site_options;
5347
	}
5348
5349
	/**
5350
	 * Fetch the filtered array of options that we should compare to determine an identity crisis.
5351
	 *
5352
	 * @return array An array of options to check.
5353
	 */
5354
	public static function identity_crisis_options_to_check() {
5355
		$options = array(
5356
			'siteurl',
5357
			'home',
5358
		);
5359
		/**
5360
		 * Filter the options that we should compare to determine an identity crisis.
5361
		 *
5362
		 * @since 2.5.0
5363
		 *
5364
		 * @param array $options Array of options to compare to determine an identity crisis.
5365
		 */
5366
		return apply_filters( 'jetpack_identity_crisis_options_to_check', $options );
5367
	}
5368
5369
	/**
5370
	 * Checks to make sure that local options have the same values as remote options.  Will cache the results for up to 24 hours.
5371
	 *
5372
	 * @param bool $force_recheck Whether to ignore any cached transient and manually re-check.
5373
	 *
5374
	 * @return array An array of options that do not match.  If everything is good, it will evaluate to false.
5375
	 */
5376
	public static function check_identity_crisis( $force_recheck = false ) {
5377
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || Jetpack::is_staging_site() )
5378
			return false;
5379
5380
		if ( $force_recheck || false === ( $errors = get_transient( 'jetpack_has_identity_crisis' ) ) ) {
5381
			$options_to_check = self::identity_crisis_options_to_check();
5382
			$cloud_options = Jetpack::init()->get_cloud_site_options( $options_to_check );
5383
			$errors        = array();
5384
5385
			foreach ( $cloud_options as $cloud_key => $cloud_value ) {
5386
5387
				// If it's not the same as the local value...
5388
				if ( $cloud_value !== get_option( $cloud_key ) ) {
5389
5390
					// Break out if we're getting errors.  We are going to check the error keys later when we alert.
5391
					if ( 'error_code' == $cloud_key ) {
5392
						$errors[ $cloud_key ] = $cloud_value;
5393
						break;
5394
					}
5395
5396
					$parsed_cloud_value = parse_url( $cloud_value );
5397
					// If the current options is an IP address
5398
					if ( filter_var( $parsed_cloud_value['host'], FILTER_VALIDATE_IP ) ) {
5399
						// Give the new value a Jetpack to fly in to the clouds
5400
						Jetpack::resolve_identity_crisis( $cloud_key );
5401
						continue;
5402
					}
5403
5404
					// And it's not been added to the whitelist...
5405
					if ( ! self::is_identity_crisis_value_whitelisted( $cloud_key, $cloud_value ) ) {
5406
						/*
5407
						 * This should be a temporary hack until a cleaner solution is found.
5408
						 *
5409
						 * The siteurl and home can be set to use http in General > Settings
5410
						 * however some constants can be defined that can force https in wp-admin
5411
						 * when this happens wpcom can confuse wporg with a fake identity
5412
						 * crisis with a mismatch of http vs https when it should be allowed.
5413
						 * we need to check that here.
5414
						 *
5415
						 * @see https://github.com/Automattic/jetpack/issues/1006
5416
						 */
5417
						if ( ( 'home' == $cloud_key || 'siteurl' == $cloud_key )
5418
							&& ( substr( $cloud_value, 0, 8 ) == "https://" )
5419
							&& Jetpack::init()->is_ssl_required_to_visit_site() ) {
5420
							// Ok, we found a mismatch of http and https because of wp-config, not an invalid url
5421
							continue;
5422
						}
5423
5424
5425
						// Then kick an error!
5426
						$errors[ $cloud_key ] = $cloud_value;
5427
					}
5428
				}
5429
			}
5430
		}
5431
5432
		/**
5433
		 * Filters the errors returned when checking for an Identity Crisis.
5434
		 *
5435
		 * @since 2.3.2
5436
		 *
5437
		 * @param array $errors Array of Identity Crisis errors.
5438
		 * @param bool $force_recheck Ignore any cached transient and manually re-check. Default to false.
5439
		 */
5440
		return apply_filters( 'jetpack_has_identity_crisis', $errors, $force_recheck );
5441
	}
5442
5443
	/*
5444
	 * Resolve ID crisis
5445
	 *
5446
	 * If the URL has changed, but the rest of the options are the same (i.e. blog/user tokens)
5447
	 * The user has the option to update the shadow site with the new URL before a new
5448
	 * token is created.
5449
	 *
5450
	 * @param $key : Which option to sync.  null defaults to home and siteurl
5451
	 */
5452
	public static function resolve_identity_crisis( $key = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $key 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...
5453
5454
		// TODO!!!
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...
5455
5456
//		if ( $key ) {
5457
//			$identity_options = array( $key );
5458
//		} else {
5459
//			$identity_options = self::identity_crisis_options_to_check();
5460
//		}
5461
//
5462
//		if ( is_array( $identity_options ) ) {
5463
//			foreach( $identity_options as $identity_option ) {
5464
//				Jetpack_Sync::sync_options( __FILE__, $identity_option );
5465
//
5466
//				/**
5467
//				 * Fires when a shadow site option is updated.
5468
//				 * These options are updated via the Identity Crisis UI.
5469
//				 * $identity_option is the option that gets updated.
5470
//				 *
5471
//				 * @since 3.7.0
5472
//				 */
5473
//				do_action( "update_option_{$identity_option}" );
5474
//			}
5475
//		}
5476
	}
5477
5478
	/*
5479
	 * Whitelist URL
5480
	 *
5481
	 * Ignore the URL differences between the blog and the shadow site.
5482
	 */
5483
	public static function whitelist_current_url() {
5484
		$options_to_check = Jetpack::identity_crisis_options_to_check();
5485
		$cloud_options = Jetpack::init()->get_cloud_site_options( $options_to_check );
5486
5487
		foreach ( $cloud_options as $cloud_key => $cloud_value ) {
5488
			Jetpack::whitelist_identity_crisis_value( $cloud_key, $cloud_value );
5489
		}
5490
	}
5491
5492
	/*
5493
	 * Ajax callbacks for ID crisis resolutions
5494
	 *
5495
	 * Things that could happen here:
5496
	 *  - site_migrated : Update the URL on the shadow blog to match new domain
5497
	 *  - whitelist     : Ignore the URL difference
5498
	 *  - default       : Error message
5499
	 */
5500
	public static function resolve_identity_crisis_ajax_callback() {
5501
		check_ajax_referer( 'resolve-identity-crisis', 'ajax-nonce' );
5502
5503
		switch ( $_POST[ 'crisis_resolution_action' ] ) {
5504
			case 'site_migrated':
5505
				Jetpack::resolve_identity_crisis();
5506
				echo 'resolved';
5507
				break;
5508
5509
			case 'whitelist':
5510
				Jetpack::whitelist_current_url();
5511
				echo 'whitelisted';
5512
				break;
5513
5514
			case 'reset_connection':
5515
				// Delete the options first so it doesn't get confused which site to disconnect dotcom-side
5516
				Jetpack_Options::delete_option(
5517
					array(
5518
						'register',
5519
						'blog_token',
5520
						'user_token',
5521
						'user_tokens',
5522
						'master_user',
5523
						'time_diff',
5524
						'fallback_no_verify_ssl_certs',
5525
						'id',
5526
					)
5527
				);
5528
				delete_transient( 'jetpack_has_identity_crisis' );
5529
5530
				echo 'reset-connection-success';
5531
				break;
5532
5533
			default:
5534
				echo 'missing action';
5535
				break;
5536
		}
5537
5538
		wp_die();
5539
	}
5540
5541
	/**
5542
	 * Adds a value to the whitelist for the specified key.
5543
	 *
5544
	 * @param string $key The option name that we're whitelisting the value for.
5545
	 * @param string $value The value that we're intending to add to the whitelist.
5546
	 *
5547
	 * @return bool Whether the value was added to the whitelist, or false if it was already there.
5548
	 */
5549
	public static function whitelist_identity_crisis_value( $key, $value ) {
5550
		if ( Jetpack::is_identity_crisis_value_whitelisted( $key, $value ) ) {
5551
			return false;
5552
		}
5553
5554
		$whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist', array() );
5555
		if ( empty( $whitelist[ $key ] ) || ! is_array( $whitelist[ $key ] ) ) {
5556
			$whitelist[ $key ] = array();
5557
		}
5558
		array_push( $whitelist[ $key ], $value );
5559
5560
		Jetpack_Options::update_option( 'identity_crisis_whitelist', $whitelist );
5561
		return true;
5562
	}
5563
5564
	/**
5565
	 * Checks whether a value is already whitelisted.
5566
	 *
5567
	 * @param string $key The option name that we're checking the value for.
5568
	 * @param string $value The value that we're curious to see if it's on the whitelist.
5569
	 *
5570
	 * @return bool Whether the value is whitelisted.
5571
	 */
5572
	public static function is_identity_crisis_value_whitelisted( $key, $value ) {
5573
		$whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist', array() );
5574
		if ( ! empty( $whitelist[ $key ] ) && is_array( $whitelist[ $key ] ) && in_array( $value, $whitelist[ $key ] ) ) {
5575
			return true;
5576
		}
5577
		return false;
5578
	}
5579
5580
	/**
5581
	 * Checks whether the home and siteurl specifically are whitelisted
5582
	 * Written so that we don't have re-check $key and $value params every time
5583
	 * we want to check if this site is whitelisted, for example in footer.php
5584
	 *
5585
	 * @return bool True = already whitelsisted False = not whitelisted
5586
	 */
5587
	public static function is_staging_site() {
5588
		$is_staging = false;
5589
5590
		$current_whitelist = Jetpack_Options::get_option( 'identity_crisis_whitelist' );
5591
		if ( $current_whitelist ) {
5592
			$options_to_check  = Jetpack::identity_crisis_options_to_check();
5593
			$cloud_options     = Jetpack::init()->get_cloud_site_options( $options_to_check );
5594
5595
			foreach ( $cloud_options as $cloud_key => $cloud_value ) {
5596
				if ( self::is_identity_crisis_value_whitelisted( $cloud_key, $cloud_value ) ) {
5597
					$is_staging = true;
5598
					break;
5599
				}
5600
			}
5601
		}
5602
		$known_staging = array(
5603
			'urls' => array(
5604
				'#\.staging\.wpengine\.com$#i',
5605
				),
5606
			'constants' => array(
5607
				'IS_WPE_SNAPSHOT',
5608
				'JETPACK_STAGING_MODE',
5609
				)
5610
			);
5611
		/**
5612
		 * Filters the flags of known staging sites.
5613
		 *
5614
		 * @since 3.9.0
5615
		 *
5616
		 * @param array $known_staging {
5617
		 *     An array of arrays that each are used to check if the current site is staging.
5618
		 *     @type array $urls      URLs of staging sites in regex to check against site_url.
5619
		 *     @type array $cosntants PHP constants of known staging/developement environments.
5620
		 *  }
5621
		 */
5622
		$known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
5623
5624
		if ( isset( $known_staging['urls'] ) ) {
5625
			foreach ( $known_staging['urls'] as $url ){
5626
				if ( preg_match( $url, site_url() ) ) {
5627
					$is_staging = true;
5628
					break;
5629
				}
5630
			}
5631
		}
5632
5633
		if ( isset( $known_staging['constants'] ) ) {
5634
			foreach ( $known_staging['constants'] as $constant ) {
5635
				if ( defined( $constant ) && constant( $constant ) ) {
5636
					$is_staging = true;
5637
				}
5638
			}
5639
		}
5640
5641
		/**
5642
		 * Filters is_staging_site check.
5643
		 *
5644
		 * @since 3.9.0
5645
		 *
5646
		 * @param bool $is_staging If the current site is a staging site.
5647
		 */
5648
		return apply_filters( 'jetpack_is_staging_site', $is_staging );
5649
	}
5650
5651
	public function identity_crisis_js( $nonce ) {
5652
?>
5653
<script>
5654
(function( $ ) {
5655
	var SECOND_IN_MS = 1000;
5656
5657
	function contactSupport( e ) {
5658
		e.preventDefault();
5659
		$( '.jp-id-crisis-question' ).hide();
5660
		$( '#jp-id-crisis-contact-support' ).show();
5661
	}
5662
5663
	function autodismissSuccessBanner() {
5664
		$( '.jp-identity-crisis' ).fadeOut(600); //.addClass( 'dismiss' );
5665
	}
5666
5667
	var data = { action: 'jetpack_resolve_identity_crisis', 'ajax-nonce': '<?php echo $nonce; ?>' };
5668
5669
	$( document ).ready(function() {
5670
5671
		// Site moved: Update the URL on the shadow blog
5672
		$( '.site-moved' ).click(function( e ) {
5673
			e.preventDefault();
5674
			data.crisis_resolution_action = 'site_migrated';
5675
			$( '#jp-id-crisis-question-1 .spinner' ).show();
5676
			$.post( ajaxurl, data, function() {
5677
				$( '.jp-id-crisis-question' ).hide();
5678
				$( '.banner-title' ).hide();
5679
				$( '#jp-id-crisis-success' ).show();
5680
				setTimeout( autodismissSuccessBanner, 6 * SECOND_IN_MS );
5681
			});
5682
5683
		});
5684
5685
		// URL hasn't changed, next question please.
5686
		$( '.site-not-moved' ).click(function( e ) {
5687
			e.preventDefault();
5688
			$( '.jp-id-crisis-question' ).hide();
5689
			$( '#jp-id-crisis-question-2' ).show();
5690
		});
5691
5692
		// Reset connection: two separate sites.
5693
		$( '.reset-connection' ).click(function( e ) {
5694
			data.crisis_resolution_action = 'reset_connection';
5695
			$.post( ajaxurl, data, function( response ) {
5696
				if ( 'reset-connection-success' === response ) {
5697
					window.location.replace( '<?php echo Jetpack::admin_url(); ?>' );
5698
				}
5699
			});
5700
		});
5701
5702
		// It's a dev environment.  Ignore.
5703
		$( '.is-dev-env' ).click(function( e ) {
5704
			data.crisis_resolution_action = 'whitelist';
5705
			$( '#jp-id-crisis-question-2 .spinner' ).show();
5706
			$.post( ajaxurl, data, function() {
5707
				$( '.jp-id-crisis-question' ).hide();
5708
				$( '.banner-title' ).hide();
5709
				$( '#jp-id-crisis-success' ).show();
5710
				setTimeout( autodismissSuccessBanner, 4 * SECOND_IN_MS );
5711
			});
5712
		});
5713
5714
		$( '.not-reconnecting' ).click(contactSupport);
5715
		$( '.not-staging-or-dev' ).click(contactSupport);
5716
	});
5717
})( jQuery );
5718
</script>
5719
<?php
5720
	}
5721
5722
	/**
5723
	 * Displays an admin_notice, alerting the user to an identity crisis.
5724
	 */
5725
	public function alert_identity_crisis() {
5726
		// @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...
5727
		if ( ! Jetpack::is_development_version() ) {
5728
			return;
5729
		}
5730
5731
		// @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...
5732
		// @see https://github.com/Automattic/jetpack/issues/2702
5733
		if ( is_multisite() && defined( 'SUNRISE' ) && ! Jetpack::is_development_version() ) {
5734
			return;
5735
		}
5736
5737
		if ( ! current_user_can( 'jetpack_disconnect' ) ) {
5738
			return;
5739
		}
5740
5741
		if ( ! $errors = self::check_identity_crisis() ) {
5742
			return;
5743
		}
5744
5745
		// Only show on dashboard and jetpack pages
5746
		$screen = get_current_screen();
5747
		if ( 'dashboard' !== $screen->base && ! did_action( 'jetpack_notices' ) ) {
5748
			return;
5749
		}
5750
5751
		// Include the js!
5752
		$ajax_nonce = wp_create_nonce( 'resolve-identity-crisis' );
5753
		$this->identity_crisis_js( $ajax_nonce );
5754
5755
		// Include the CSS!
5756
		if ( ! wp_script_is( 'jetpack', 'done' ) ) {
5757
			$this->admin_banner_styles();
5758
		}
5759
5760
		if ( ! array_key_exists( 'error_code', $errors ) ) {
5761
			$key = 'siteurl';
5762
			if ( ! $errors[ $key ] ) {
5763
				$key = 'home';
5764
			}
5765
		} else {
5766
			$key = 'error_code';
5767
			// 401 is the only error we care about.  Any other errors should not trigger the alert.
5768
			if ( 401 !== $errors[ $key ] ) {
5769
				return;
5770
			}
5771
		}
5772
5773
		?>
5774
5775
		<style>
5776
			.jp-identity-crisis .jp-btn-group {
5777
					margin: 15px 0;
5778
				}
5779
			.jp-identity-crisis strong {
5780
					color: #518d2a;
5781
				}
5782
			.jp-identity-crisis.dismiss {
5783
				display: none;
5784
			}
5785
			.jp-identity-crisis .button {
5786
				margin-right: 4px;
5787
			}
5788
		</style>
5789
5790
		<div id="message" class="error jetpack-message jp-identity-crisis stay-visible">
5791
			<div class="service-mark"></div>
5792
			<div class="jp-id-banner__content">
5793
				<!-- <h3 class="banner-title"><?php _e( 'Something\'s not quite right with your Jetpack connection! Let\'s fix that.', 'jetpack' ); ?></h3> -->
5794
5795
				<div class="jp-id-crisis-question" id="jp-id-crisis-question-1">
5796
					<?php
5797
					// 401 means that this site has been disconnected from wpcom, but the remote site still thinks it's connected.
5798
					if ( 'error_code' == $key && '401' == $errors[ $key ] ) : ?>
5799
						<div class="banner-content">
5800
							<p><?php
5801
								/* translators: %s is a URL */
5802
								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/' );
5803
							?></p>
5804
						</div>
5805
						<div class="jp-btn-group">
5806
							<a href="#" class="reset-connection"><?php _e( 'Reset the connection', 'jetpack' ); ?></a>
5807
							<span class="idc-separator">|</span>
5808
							<a href="<?php echo esc_url( wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=dismiss' ), 'jetpack-deactivate' ) ); ?>"><?php _e( 'Deactivate Jetpack', 'jetpack' ); ?></a>
5809
						</div>
5810
					<?php else : ?>
5811
							<div class="banner-content">
5812
							<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>
5813
							</div>
5814
						<div class="jp-btn-group">
5815
							<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>
5816
							<span class="spinner"></span>
5817
						</div>
5818
					<?php endif ; ?>
5819
				</div>
5820
5821
				<div class="jp-id-crisis-question" id="jp-id-crisis-question-2" style="display: none;">
5822
					<div class="banner-content">
5823
						<p><?php printf(
5824
							/* translators: %1$s, %2$s and %3$s are URLs */
5825
							__(
5826
								'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>',
5827
								'jetpack'
5828
							),
5829
							$errors[ $key ],
5830
							(string) get_option( $key ),
5831
							'https://jetpack.me/support/what-does-resetting-the-connection-mean/'
5832
						); ?></p>
5833
					</div>
5834
					<div class="jp-btn-group">
5835
						<a href="#" class="reset-connection"><?php _e( 'Reset the connection', 'jetpack' ); ?></a> <span class="idc-separator">|</span>
5836
						<a href="#" class="is-dev-env"><?php _e( 'This is a development environment', 'jetpack' ); ?></a> <span class="idc-separator">|</span>
5837
						<a href="https://jetpack.me/contact-support/" class="contact-support"><?php _e( 'Submit a support ticket', 'jetpack' ); ?></a>
5838
						<span class="spinner"></span>
5839
					</div>
5840
				</div>
5841
5842
				<div class="jp-id-crisis-success" id="jp-id-crisis-success" style="display: none;">
5843
					<h3 class="success-notice"><?php printf( __( 'Thanks for taking the time to sort things out. We&#039;ve updated our records accordingly!', 'jetpack' ) ); ?></h3>
5844
				</div>
5845
			</div>
5846
		</div>
5847
5848
		<?php
5849
	}
5850
5851
	/**
5852
	 * Maybe Use a .min.css stylesheet, maybe not.
5853
	 *
5854
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
5855
	 */
5856
	public static function maybe_min_asset( $url, $path, $plugin ) {
5857
		// Short out on things trying to find actual paths.
5858
		if ( ! $path || empty( $plugin ) ) {
5859
			return $url;
5860
		}
5861
5862
		// Strip out the abspath.
5863
		$base = dirname( plugin_basename( $plugin ) );
5864
5865
		// Short out on non-Jetpack assets.
5866
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
5867
			return $url;
5868
		}
5869
5870
		// File name parsing.
5871
		$file              = "{$base}/{$path}";
5872
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
5873
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
5874
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
5875
		$extension         = array_shift( $file_name_parts_r );
5876
5877
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
5878
			// Already pointing at the minified version.
5879
			if ( 'min' === $file_name_parts_r[0] ) {
5880
				return $url;
5881
			}
5882
5883
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
5884
			if ( file_exists( $min_full_path ) ) {
5885
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
5886
			}
5887
		}
5888
5889
		return $url;
5890
	}
5891
5892
	/**
5893
	 * Maybe inlines a stylesheet.
5894
	 *
5895
	 * If you'd like to inline a stylesheet instead of printing a link to it,
5896
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
5897
	 *
5898
	 * Attached to `style_loader_tag` filter.
5899
	 *
5900
	 * @param string $tag The tag that would link to the external asset.
5901
	 * @param string $handle The registered handle of the script in question.
5902
	 *
5903
	 * @return string
5904
	 */
5905
	public static function maybe_inline_style( $tag, $handle ) {
5906
		global $wp_styles;
5907
		$item = $wp_styles->registered[ $handle ];
5908
5909
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
5910
			return $tag;
5911
		}
5912
5913
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
5914
			$href = $matches[1];
5915
			// Strip off query string
5916
			if ( $pos = strpos( $href, '?' ) ) {
5917
				$href = substr( $href, 0, $pos );
5918
			}
5919
			// Strip off fragment
5920
			if ( $pos = strpos( $href, '#' ) ) {
5921
				$href = substr( $href, 0, $pos );
5922
			}
5923
		} else {
5924
			return $tag;
5925
		}
5926
5927
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
5928
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
5929
			return $tag;
5930
		}
5931
5932
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
5933
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
5934
			// And this isn't the pass that actually deals with the RTL version...
5935
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
5936
				// Short out, as the RTL version will deal with it in a moment.
5937
				return $tag;
5938
			}
5939
		}
5940
5941
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
5942
		$css  = Jetpack::absolutize_css_urls( file_get_contents( $file ), $href );
5943
		if ( $css ) {
5944
			$tag = "<!-- Inline {$item->handle} -->\r\n";
5945
			if ( empty( $item->extra['after'] ) ) {
5946
				wp_add_inline_style( $handle, $css );
5947
			} else {
5948
				array_unshift( $item->extra['after'], $css );
5949
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
5950
			}
5951
		}
5952
5953
		return $tag;
5954
	}
5955
5956
	/**
5957
	 * Loads a view file from the views
5958
	 *
5959
	 * Data passed in with the $data parameter will be available in the
5960
	 * template file as $data['value']
5961
	 *
5962
	 * @param string $template - Template file to load
5963
	 * @param array $data - Any data to pass along to the template
5964
	 * @return boolean - If template file was found
5965
	 **/
5966
	public function load_view( $template, $data = array() ) {
5967
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
5968
5969
		if( file_exists( $views_dir . $template ) ) {
5970
			require_once( $views_dir . $template );
5971
			return true;
5972
		}
5973
5974
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
5975
		return false;
5976
	}
5977
5978
	/**
5979
	 * Sends a ping to the Jetpack servers to toggle on/off remote portions
5980
	 * required by some modules.
5981
	 *
5982
	 * @param string $module_slug
5983
	 */
5984
	public function toggle_module_on_wpcom( $module_slug ) {
5985
		if ( false !== strpos( current_filter(), 'jetpack_activate_module_' ) ) {
5986
			self::check_privacy( $module_slug );
5987
		}
5988
	}
5989
5990
	/**
5991
	 * Throws warnings for deprecated hooks to be removed from Jetpack
5992
	 */
5993
	public function deprecated_hooks() {
5994
		global $wp_filter;
5995
5996
		/*
5997
		 * Format:
5998
		 * deprecated_filter_name => replacement_name
5999
		 *
6000
		 * If there is no replacement us null for replacement_name
6001
		 */
6002
		$deprecated_list = array(
6003
			'jetpack_bail_on_shortcode' => 'jetpack_shortcodes_to_include',
6004
			'wpl_sharing_2014_1'        => null,
6005
			'jetpack-tools-to-include'  => 'jetpack_tools_to_include',
6006
		);
6007
6008
		// This is a silly loop depth. Better way?
6009
		foreach( $deprecated_list AS $hook => $hook_alt ) {
6010
			if( isset( $wp_filter[ $hook ] ) && is_array( $wp_filter[ $hook ] ) ) {
6011
				foreach( $wp_filter[$hook] AS $func => $values ) {
6012
					foreach( $values AS $hooked ) {
6013
						_deprecated_function( $hook . ' used for ' . $hooked['function'], null, $hook_alt );
6014
					}
6015
				}
6016
			}
6017
		}
6018
	}
6019
6020
	/**
6021
	 * Converts any url in a stylesheet, to the correct absolute url.
6022
	 *
6023
	 * Considerations:
6024
	 *  - Normal, relative URLs     `feh.png`
6025
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6026
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6027
	 *  - Absolute URLs             `http://domain.com/feh.png`
6028
	 *  - Domain root relative URLs `/feh.png`
6029
	 *
6030
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6031
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6032
	 *
6033
	 * @return mixed|string
6034
	 */
6035
	public static function absolutize_css_urls( $css, $css_file_url ) {
6036
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6037
		$css_dir = dirname( $css_file_url );
6038
		$p       = parse_url( $css_dir );
6039
		$domain  = sprintf(
6040
					'%1$s//%2$s%3$s%4$s',
6041
					isset( $p['scheme'] )           ? "{$p['scheme']}:" : '',
6042
					isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6043
					$p['host'],
6044
					isset( $p['port'] )             ? ":{$p['port']}" : ''
6045
				);
6046
6047
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6048
			$find = $replace = array();
6049
			foreach ( $matches as $match ) {
6050
				$url = trim( $match['path'], "'\" \t" );
6051
6052
				// If this is a data url, we don't want to mess with it.
6053
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6054
					continue;
6055
				}
6056
6057
				// If this is an absolute or protocol-agnostic url,
6058
				// we don't want to mess with it.
6059
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6060
					continue;
6061
				}
6062
6063
				switch ( substr( $url, 0, 1 ) ) {
6064
					case '/':
6065
						$absolute = $domain . $url;
6066
						break;
6067
					default:
6068
						$absolute = $css_dir . '/' . $url;
6069
				}
6070
6071
				$find[]    = $match[0];
6072
				$replace[] = sprintf( 'url("%s")', $absolute );
6073
			}
6074
			$css = str_replace( $find, $replace, $css );
6075
		}
6076
6077
		return $css;
6078
	}
6079
6080
	/**
6081
	 * This method checks to see if SSL is required by the site in
6082
	 * order to visit it in some way other than only setting the
6083
	 * https value in the home or siteurl values.
6084
	 *
6085
	 * @since 3.2
6086
	 * @return boolean
6087
	 **/
6088
	private function is_ssl_required_to_visit_site() {
6089
		global $wp_version;
6090
		$ssl = is_ssl();
6091
6092
		if ( force_ssl_admin() ) {
6093
			$ssl = true;
6094
		}
6095
		return $ssl;
6096
	}
6097
6098
	/**
6099
	 * This methods removes all of the registered css files on the frontend
6100
	 * from Jetpack in favor of using a single file. In effect "imploding"
6101
	 * all the files into one file.
6102
	 *
6103
	 * Pros:
6104
	 * - Uses only ONE css asset connection instead of 15
6105
	 * - Saves a minimum of 56k
6106
	 * - Reduces server load
6107
	 * - Reduces time to first painted byte
6108
	 *
6109
	 * Cons:
6110
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6111
	 *		should not cause any issues with themes.
6112
	 * - Plugins/themes dequeuing styles no longer do anything. See
6113
	 *		jetpack_implode_frontend_css filter for a workaround
6114
	 *
6115
	 * For some situations developers may wish to disable css imploding and
6116
	 * instead operate in legacy mode where each file loads seperately and
6117
	 * can be edited individually or dequeued. This can be accomplished with
6118
	 * the following line:
6119
	 *
6120
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6121
	 *
6122
	 * @since 3.2
6123
	 **/
6124
	public function implode_frontend_css( $travis_test = false ) {
6125
		$do_implode = true;
6126
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6127
			$do_implode = false;
6128
		}
6129
6130
		/**
6131
		 * Allow CSS to be concatenated into a single jetpack.css file.
6132
		 *
6133
		 * @since 3.2.0
6134
		 *
6135
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6136
		 */
6137
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6138
6139
		// Do not use the imploded file when default behaviour was altered through the filter
6140
		if ( ! $do_implode ) {
6141
			return;
6142
		}
6143
6144
		// We do not want to use the imploded file in dev mode, or if not connected
6145
		if ( Jetpack::is_development_mode() || ! self::is_active() ) {
6146
			if ( ! $travis_test ) {
6147
				return;
6148
			}
6149
		}
6150
6151
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6152
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6153
			return;
6154
		}
6155
6156
		/*
6157
		 * Now we assume Jetpack is connected and able to serve the single
6158
		 * file.
6159
		 *
6160
		 * In the future there will be a check here to serve the file locally
6161
		 * or potentially from the Jetpack CDN
6162
		 *
6163
		 * For now:
6164
		 * - Enqueue a single imploded css file
6165
		 * - Zero out the style_loader_tag for the bundled ones
6166
		 * - Be happy, drink scotch
6167
		 */
6168
6169
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6170
6171
		$version = Jetpack::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6172
6173
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6174
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6175
	}
6176
6177
	function concat_remove_style_loader_tag( $tag, $handle ) {
6178
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6179
			$tag = '';
6180
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6181
				$tag = "<!-- `" . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6182
			}
6183
		}
6184
6185
		return $tag;
6186
	}
6187
6188
	/*
6189
	 * Check the heartbeat data
6190
	 *
6191
	 * Organizes the heartbeat data by severity.  For example, if the site
6192
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6193
	 *
6194
	 * Data will be added to "caution" array, if it either:
6195
	 *  - Out of date Jetpack version
6196
	 *  - Out of date WP version
6197
	 *  - Out of date PHP version
6198
	 *
6199
	 * $return array $filtered_data
6200
	 */
6201
	public static function jetpack_check_heartbeat_data() {
6202
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6203
6204
		$good    = array();
6205
		$caution = array();
6206
		$bad     = array();
6207
6208
		foreach ( $raw_data as $stat => $value ) {
6209
6210
			// Check jetpack version
6211
			if ( 'version' == $stat ) {
6212
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6213
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__VERSION;
6214
					continue;
6215
				}
6216
			}
6217
6218
			// Check WP version
6219
			if ( 'wp-version' == $stat ) {
6220
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6221
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_WP_VERSION;
6222
					continue;
6223
				}
6224
			}
6225
6226
			// Check PHP version
6227
			if ( 'php-version' == $stat ) {
6228
				if ( version_compare( PHP_VERSION, '5.2.4', '<' ) ) {
6229
					$caution[ $stat ] = $value . " - min supported is 5.2.4";
6230
					continue;
6231
				}
6232
			}
6233
6234
			// Check ID crisis
6235
			if ( 'identitycrisis' == $stat ) {
6236
				if ( 'yes' == $value ) {
6237
					$bad[ $stat ] = $value;
6238
					continue;
6239
				}
6240
			}
6241
6242
			// The rest are good :)
6243
			$good[ $stat ] = $value;
6244
		}
6245
6246
		$filtered_data = array(
6247
			'good'    => $good,
6248
			'caution' => $caution,
6249
			'bad'     => $bad
6250
		);
6251
6252
		return $filtered_data;
6253
	}
6254
6255
6256
	/*
6257
	 * This method is used to organize all options that can be reset
6258
	 * without disconnecting Jetpack.
6259
	 *
6260
	 * It is used in class.jetpack-cli.php to reset options
6261
	 *
6262
	 * @return array of options to delete.
6263
	 */
6264
	public static function get_jetpack_options_for_reset() {
6265
		$jetpack_options            = Jetpack_Options::get_option_names();
6266
		$jetpack_options_non_compat = Jetpack_Options::get_option_names( 'non_compact' );
6267
		$jetpack_options_private    = Jetpack_Options::get_option_names( 'private' );
6268
6269
		$all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
6270
6271
		// A manual build of the wp options
6272
		$wp_options = array(
6273
			'sharing-options',
6274
			'disabled_likes',
6275
			'disabled_reblogs',
6276
			'jetpack_comments_likes_enabled',
6277
			'wp_mobile_excerpt',
6278
			'wp_mobile_featured_images',
6279
			'wp_mobile_app_promos',
6280
			'stats_options',
6281
			'stats_dashboard_widget',
6282
			'safecss_preview_rev',
6283
			'safecss_rev',
6284
			'safecss_revision_migrated',
6285
			'nova_menu_order',
6286
			'jetpack_portfolio',
6287
			'jetpack_portfolio_posts_per_page',
6288
			'jetpack_testimonial',
6289
			'jetpack_testimonial_posts_per_page',
6290
			'wp_mobile_custom_css',
6291
			'sharedaddy_disable_resources',
6292
			'sharing-options',
6293
			'sharing-services',
6294
			'site_icon_temp_data',
6295
			'featured-content',
6296
			'site_logo',
6297
		);
6298
6299
		// Flag some Jetpack options as unsafe
6300
		$unsafe_options = array(
6301
			'id',                           // (int)    The Client ID/WP.com Blog ID of this site.
6302
			'master_user',                  // (int)    The local User ID of the user who connected this site to jetpack.wordpress.com.
6303
			'version',                      // (string) Used during upgrade procedure to auto-activate new modules. version:time
6304
			'jumpstart',                    // (string) A flag for whether or not to show the Jump Start.  Accepts: new_connection, jumpstart_activated, jetpack_action_taken, jumpstart_dismissed.
6305
6306
			// non_compact
6307
			'activated',
6308
6309
			// private
6310
			'register',
6311
			'blog_token',                  // (string) The Client Secret/Blog Token of this site.
6312
			'user_token',                  // (string) The User Token of this site. (deprecated)
6313
			'user_tokens'
6314
		);
6315
6316
		// Remove the unsafe Jetpack options
6317
		foreach ( $unsafe_options as $unsafe_option ) {
6318
			if ( false !== ( $key = array_search( $unsafe_option, $all_jp_options ) ) ) {
6319
				unset( $all_jp_options[ $key ] );
6320
			}
6321
		}
6322
6323
		$options = array(
6324
			'jp_options' => $all_jp_options,
6325
			'wp_options' => $wp_options
6326
		);
6327
6328
		return $options;
6329
	}
6330
6331
	/*
6332
	 * Check if an option of a Jetpack module has been updated.
6333
	 *
6334
	 * If any module option has been updated before Jump Start has been dismissed,
6335
	 * update the 'jumpstart' option so we can hide Jump Start.
6336
	 */
6337
	public static function jumpstart_has_updated_module_option( $option_name = '' ) {
6338
		// Bail if Jump Start has already been dismissed
6339
		if ( 'new_connection' !== Jetpack::get_option( 'jumpstart' ) ) {
6340
			return false;
6341
		}
6342
6343
		$jetpack = Jetpack::init();
6344
6345
6346
		// Manual build of module options
6347
		$option_names = self::get_jetpack_options_for_reset();
6348
6349
		if ( in_array( $option_name, $option_names['wp_options'] ) ) {
6350
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
6351
6352
			//Jump start is being dismissed send data to MC Stats
6353
			$jetpack->stat( 'jumpstart', 'manual,'.$option_name );
6354
6355
			$jetpack->do_stats( 'server_side' );
6356
		}
6357
6358
	}
6359
6360
	/*
6361
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6362
	 * so we can bring them directly to their site in calypso.
6363
	 *
6364
	 * @param string | url
6365
	 * @return string | url without the guff
6366
	 */
6367
	public static function build_raw_urls( $url ) {
6368
		$strip_http = '/.*?:\/\//i';
6369
		$url = preg_replace( $strip_http, '', $url  );
6370
		$url = str_replace( '/', '::', $url );
6371
		return $url;
6372
	}
6373
6374
	/**
6375
	 * Stores and prints out domains to prefetch for page speed optimization.
6376
	 *
6377
	 * @param mixed $new_urls
6378
	 */
6379
	public static function dns_prefetch( $new_urls = null ) {
6380
		static $prefetch_urls = array();
6381
		if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) {
6382
			echo "\r\n";
6383
			foreach ( $prefetch_urls as $this_prefetch_url ) {
6384
				printf( "<link rel='dns-prefetch' href='%s'>\r\n", esc_attr( $this_prefetch_url ) );
6385
			}
6386
		} elseif ( ! empty( $new_urls ) ) {
6387
			if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) {
6388
				add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) );
6389
			}
6390
			foreach ( (array) $new_urls as $this_new_url ) {
6391
				$prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) );
6392
			}
6393
			$prefetch_urls = array_unique( $prefetch_urls );
6394
		}
6395
	}
6396
6397
	public function wp_dashboard_setup() {
6398
		if ( self::is_active() ) {
6399
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6400
			$widget_title = __( 'Site Stats', 'jetpack' );
6401
		} elseif ( ! self::is_development_mode() && current_user_can( 'jetpack_connect' ) ) {
6402
			add_action( 'jetpack_dashboard_widget', array( $this, 'dashboard_widget_connect_to_wpcom' ) );
6403
			$widget_title = __( 'Please Connect Jetpack', 'jetpack' );
6404
		}
6405
6406
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6407
			wp_add_dashboard_widget(
6408
				'jetpack_summary_widget',
6409
				$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...
6410
				array( __CLASS__, 'dashboard_widget' )
6411
			);
6412
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6413
6414
			// If we're inactive and not in development mode, sort our box to the top.
6415
			if ( ! self::is_active() && ! self::is_development_mode() ) {
6416
				global $wp_meta_boxes;
6417
6418
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6419
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6420
6421
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6422
			}
6423
		}
6424
	}
6425
6426
	/**
6427
	 * @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...
6428
	 * @return mixed
6429
	 */
6430
	function get_user_option_meta_box_order_dashboard( $sorted ) {
6431
		if ( ! is_array( $sorted ) ) {
6432
			return $sorted;
6433
		}
6434
6435
		foreach ( $sorted as $box_context => $ids ) {
6436
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6437
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6438
				continue;
6439
			}
6440
6441
			$ids_array = explode( ',', $ids );
6442
			$key = array_search( 'dashboard_stats', $ids_array );
6443
6444
			if ( false !== $key ) {
6445
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
6446
				$ids_array[ $key ] = 'jetpack_summary_widget';
6447
				$sorted[ $box_context ] = implode( ',', $ids_array );
6448
				// We've found it, stop searching, and just return.
6449
				break;
6450
			}
6451
		}
6452
6453
		return $sorted;
6454
	}
6455
6456
	public static function dashboard_widget() {
6457
		/**
6458
		 * Fires when the dashboard is loaded.
6459
		 *
6460
		 * @since 3.4.0
6461
		 */
6462
		do_action( 'jetpack_dashboard_widget' );
6463
	}
6464
6465
	public static function dashboard_widget_footer() {
6466
		?>
6467
		<footer>
6468
6469
		<div class="protect">
6470
			<?php if ( Jetpack::is_module_active( 'protect' ) ) : ?>
6471
				<h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3>
6472
				<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>
6473
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! self::is_development_mode() ) : ?>
6474
				<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' ); ?>">
6475
					<?php esc_html_e( 'Activate Protect', 'jetpack' ); ?>
6476
				</a>
6477
			<?php else : ?>
6478
				<?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?>
6479
			<?php endif; ?>
6480
		</div>
6481
6482
		<div class="akismet">
6483
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
6484
				<h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3>
6485
				<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>
6486 View Code Duplication
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
6487
				<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">
6488
					<?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?>
6489
				</a>
6490
			<?php else : ?>
6491
				<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>
6492
			<?php endif; ?>
6493
		</div>
6494
6495
6496 View Code Duplication
		<?php if ( ! current_user_can( 'edit_posts' ) && self::is_user_connected() ) : ?>
6497
			<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>
6498
		<?php endif; ?>
6499
6500
		</footer>
6501
		<?php
6502
	}
6503
6504
	public function dashboard_widget_connect_to_wpcom() {
6505
		if ( Jetpack::is_active() || Jetpack::is_development_mode() || ! current_user_can( 'jetpack_connect' ) ) {
6506
			return;
6507
		}
6508
		?>
6509
		<div class="wpcom-connect">
6510
			<div class="jp-emblem">
6511
			<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">
6512
				<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"/>
6513
			</svg>
6514
			</div>
6515
			<h3><?php esc_html_e( 'Please Connect Jetpack', 'jetpack' ); ?></h3>
6516
			<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>
6517
6518
			<div class="actions">
6519
				<a href="<?php echo $this->build_connect_url() ?>" class="button button-primary">
6520
					<?php esc_html_e( 'Connect Jetpack', 'jetpack' ); ?>
6521
				</a>
6522
			</div>
6523
		</div>
6524
		<?php
6525
	}
6526
6527
	/*
6528
	 * A graceful transition to using Core's site icon.
6529
	 *
6530
	 * All of the hard work has already been done with the image
6531
	 * in all_done_page(). All that needs to be done now is update
6532
	 * the option and display proper messaging.
6533
	 *
6534
	 * @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...
6535
	 *
6536
	 * @since 3.6.1
6537
	 *
6538
	 * @return bool false = Core's icon not available || true = Core's icon is available
6539
	 */
6540
	public static function jetpack_site_icon_available_in_core() {
6541
		global $wp_version;
6542
		$core_icon_available = function_exists( 'has_site_icon' ) && version_compare( $wp_version, '4.3-beta' ) >= 0;
6543
6544
		if ( ! $core_icon_available ) {
6545
			return false;
6546
		}
6547
6548
		// No need for Jetpack's site icon anymore if core's is already set
6549
		if ( has_site_icon() ) {
6550
			if ( Jetpack::is_module_active( 'site-icon' ) ) {
6551
				Jetpack::log( 'deactivate', 'site-icon' );
6552
				Jetpack::deactivate_module( 'site-icon' );
6553
			}
6554
			return true;
6555
		}
6556
6557
		// Transfer Jetpack's site icon to use core.
6558
		$site_icon_id = Jetpack::get_option( 'site_icon_id' );
6559
		if ( $site_icon_id ) {
6560
			// Update core's site icon
6561
			update_option( 'site_icon', $site_icon_id );
6562
6563
			// Delete Jetpack's icon option. We still want the blavatar and attached data though.
6564
			delete_option( 'site_icon_id' );
6565
		}
6566
6567
		// No need for Jetpack's site icon anymore
6568
		if ( Jetpack::is_module_active( 'site-icon' ) ) {
6569
			Jetpack::log( 'deactivate', 'site-icon' );
6570
			Jetpack::deactivate_module( 'site-icon' );
6571
		}
6572
6573
		return true;
6574
	}
6575
6576
}
6577