Completed
Push — fix/jetpack-site-endpoint ( 1da352...19b537 )
by
unknown
10:08
created

Jetpack::xmlrpc_methods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
Options:
5
jetpack_options (array)
6
	An array of options.
7
	@see Jetpack_Options::get_option_names()
8
9
jetpack_register (string)
10
	Temporary verification secrets.
11
12
jetpack_activated (int)
13
	1: the plugin was activated normally
14
	2: the plugin was activated on this site because of a network-wide activation
15
	3: the plugin was auto-installed
16
	4: the plugin was manually disconnected (but is still installed)
17
18
jetpack_active_modules (array)
19
	Array of active module slugs.
20
21
jetpack_do_activate (bool)
22
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
23
*/
24
25
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
	public $capability_translations = array(
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
			self::$instance = new Jetpack;
311
312
			self::$instance->plugin_upgrade();
313
314
			add_action( 'init', array( __CLASS__, 'perform_security_reporting' ) );
315
316
		}
317
318
		return self::$instance;
319
	}
320
321
	/**
322
	 * Must never be called statically
323
	 */
324
	function plugin_upgrade() {
325
		if ( Jetpack::is_active() ) {
326
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
327
			if ( JETPACK__VERSION != $version ) {
328
329
				// Check which active modules actually exist and remove others from active_modules list
330
				$unfiltered_modules = Jetpack::get_active_modules();
331
				$modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
332
				if ( array_diff( $unfiltered_modules, $modules ) ) {
333
					Jetpack_Options::update_option( 'active_modules', $modules );
334
				}
335
336
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
337
				/**
338
				 * Fires when synchronizing all registered options and constants.
339
				 *
340
				 * @since 3.3.0
341
				 */
342
				do_action( 'jetpack_sync_all_registered_options' );
343
			}
344
		}
345
	}
346
347
	static function activate_manage( ) {
348
349
		if ( did_action( 'init' ) || current_filter() == 'init' ) {
350
			self::activate_module( 'manage', false, false );
351
		} else if ( !  has_action( 'init' , array( __CLASS__, 'activate_manage' ) ) ) {
352
			add_action( 'init', array( __CLASS__, 'activate_manage' ) );
353
		}
354
355
	}
356
357
	/**
358
	 * Constructor.  Initializes WordPress hooks
359
	 */
360
	private function __construct() {
361
		/*
362
		 * Check for and alert any deprecated hooks
363
		 */
364
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
365
366
		/**
367
		 * Trigger a wp_version sync when updating WP versions
368
		 **/
369
		add_action( 'upgrader_process_complete', array( 'Jetpack', 'update_get_wp_version' ), 10, 2 );
370
371
		/*
372
		 * Load things that should only be in Network Admin.
373
		 *
374
		 * For now blow away everything else until a more full
375
		 * understanding of what is needed at the network level is
376
		 * available
377
		 */
378
		if( is_multisite() ) {
379
			Jetpack_Network::init();
380
		}
381
382
		// Unlink user before deleting the user from .com
383
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
384
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
385
386
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
387
			@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...
388
389
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
390
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
391
392
			$this->require_jetpack_authentication();
393
394
			if ( Jetpack::is_active() ) {
395
				// Hack to preserve $HTTP_RAW_POST_DATA
396
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
397
398
				$signed = $this->verify_xml_rpc_signature();
399
				if ( $signed && ! is_wp_error( $signed ) ) {
400
					// The actual API methods.
401
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
402
				} else {
403
					// The jetpack.authorize method should be available for unauthenticated users on a site with an
404
					// active Jetpack connection, so that additional users can link their account.
405
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'authorize_xmlrpc_methods' ) );
406
				}
407
			} else {
408
				// The bootstrap API methods.
409
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
410
			}
411
412
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
413
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
414
		} elseif ( is_admin() && isset( $_POST['action'] ) && 'jetpack_upload_file' == $_POST['action'] ) {
415
			$this->require_jetpack_authentication();
416
			$this->add_remote_request_handlers();
417
		} else {
418
			if ( Jetpack::is_active() ) {
419
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
420
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
421
			}
422
		}
423
424
		if ( Jetpack::is_active() ) {
425
			Jetpack_Heartbeat::init();
426
		}
427
428
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
429
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
430
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
431
		}
432
433
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
434
435
		add_action( 'admin_init', array( $this, 'admin_init' ) );
436
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
437
438
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
439
440
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
441
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
442
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
443
444
		add_action( 'wp_ajax_jetpack-sync-reindex-trigger', array( $this, 'sync_reindex_trigger' ) );
445
		add_action( 'wp_ajax_jetpack-sync-reindex-status', array( $this, 'sync_reindex_status' ) );
446
447
		// If any module option is updated before Jump Start is dismissed, hide Jump Start.
448
		add_action( 'update_option', array( $this, 'jumpstart_has_updated_module_option' ) );
449
450
		// Identity Crisis AJAX callback function
451
		add_action( 'wp_ajax_jetpack_resolve_identity_crisis', array( $this, 'resolve_identity_crisis_ajax_callback' ) );
452
453
		// JITM AJAX callback function
454
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
455
456
		add_action( 'wp_ajax_jetpack_admin_ajax',          array( $this, 'jetpack_admin_ajax_callback' ) );
457
		add_action( 'wp_ajax_jetpack_admin_ajax_refresh',  array( $this, 'jetpack_admin_ajax_refresh_data' ) );
458
459
		// Universal ajax callback for all tracking events triggered via js
460
		add_action( 'wp_ajax_jetpack_tracks', array( $this, 'jetpack_admin_ajax_tracks_callback' ) );
461
462
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
463
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
464
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
465
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
466
467
		add_action( 'jetpack_activate_module', array( $this, 'activate_module_actions' ) );
468
469
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
470
		
471
		/**
472
		 * These actions run checks to load additional files.
473
		 * They check for external files or plugins, so they need to run as late as possible.
474
		 */
475
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
476
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
477
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
478
479
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
480
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
481
482
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
483
484
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
485
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
486
487
		// A filter to control all just in time messages
488
		add_filter( 'jetpack_just_in_time_msgs', '__return_false' );
489
490
		/**
491
		 * This is the hack to concatinate all css files into one.
492
		 * For description and reasoning see the implode_frontend_css method
493
		 *
494
		 * Super late priority so we catch all the registered styles
495
		 */
496
		if( !is_admin() ) {
497
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
498
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
499
		}
500
501
	}
502
	
503
	function jetpack_admin_ajax_tracks_callback() {
504
		// Check for nonce
505
		if ( ! isset( $_REQUEST['tracksNonce'] ) || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' ) ) {
506
			wp_die( 'Permissions check failed.' );
507
		}
508
509
		if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] )  ) {
510
			wp_die( 'No valid event name or type.' );
511
		}
512
513
		$tracks_data = array();
514
		if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
515
			$tracks_data = array( 'clicked' => $_REQUEST['tracksEventProp'] );
516
		}
517
518
		JetpackTracking::record_user_event( $_REQUEST['tracksEventName'], $tracks_data );
519
		wp_send_json_success();
520
		wp_die();
521
	}
522
523
	function jetpack_admin_ajax_callback() {
524
		// Check for nonce
525 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) || ! current_user_can( 'jetpack_manage_modules' ) ) {
526
			wp_die( 'permissions check failed' );
527
		}
528
529
		if ( isset( $_REQUEST['toggleModule'] ) && 'nux-toggle-module' == $_REQUEST['toggleModule'] ) {
530
			$slug = $_REQUEST['thisModuleSlug'];
531
532
			if ( ! in_array( $slug, Jetpack::get_available_modules() ) ) {
533
				wp_die( 'That is not a Jetpack module slug' );
534
			}
535
536
			if ( Jetpack::is_module_active( $slug ) ) {
537
				Jetpack::deactivate_module( $slug );
538
			} else {
539
				Jetpack::activate_module( $slug, false, false );
540
			}
541
542
			$modules = Jetpack_Admin::init()->get_modules();
543
			echo json_encode( $modules[ $slug ] );
544
545
			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...
546
		}
547
548
		wp_die();
549
	}
550
551
	/*
552
	 * Sometimes we need to refresh the data,
553
	 * especially if the page is visited via a 'history'
554
	 * event like back/forward
555
	 */
556
	function jetpack_admin_ajax_refresh_data() {
557
		// Check for nonce
558 View Code Duplication
		if ( ! isset( $_REQUEST['adminNonce'] ) || ! wp_verify_nonce( $_REQUEST['adminNonce'], 'jetpack-admin-nonce' ) ) {
559
			wp_die( 'permissions check failed' );
560
		}
561
562
		if ( isset( $_REQUEST['refreshData'] ) && 'refresh' == $_REQUEST['refreshData'] ) {
563
			$modules = Jetpack_Admin::init()->get_modules();
564
			echo json_encode( $modules );
565
			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...
566
		}
567
568
		wp_die();
569
	}
570
571
	/**
572
	 * The callback for the JITM ajax requests.
573
	 */
574
	function jetpack_jitm_ajax_callback() {
575
		// Check for nonce
576
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
577
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
578
		}
579
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
580
			$module_slug = $_REQUEST['jitmModule'];
581
			Jetpack::log( 'activate', $module_slug );
582
			Jetpack::activate_module( $module_slug, false, false );
583
			Jetpack::state( 'message', 'no_message' );
584
585
			//A Jetpack module is being activated through a JITM, track it
586
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
587
			$this->do_stats( 'server_side' );
588
589
			wp_send_json_success();
590
		}
591
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
592
			// get the hide_jitm options array
593
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
594
			$module_slug = $_REQUEST['jitmModule'];
595
596
			if( ! $jetpack_hide_jitm ) {
597
				$jetpack_hide_jitm = array(
598
					$module_slug => 'hide'
599
				);
600
			} else {
601
				$jetpack_hide_jitm[$module_slug] = 'hide';
602
			}
603
604
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
605
606
			//jitm is being dismissed forever, track it
607
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
608
			$this->do_stats( 'server_side' );
609
610
			wp_send_json_success();
611
		}
612 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
613
			$module_slug = $_REQUEST['jitmModule'];
614
615
			// User went to WordPress.com, track this
616
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
617
			$this->do_stats( 'server_side' );
618
619
			wp_send_json_success();
620
		}
621 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'viewed' == $_REQUEST['jitmActionToTake'] ) {
622
			$track = $_REQUEST['jitmModule'];
623
624
			// User is viewing JITM, track it.
625
			$this->stat( 'jitm', $track . '-viewed-' . JETPACK__VERSION );
626
			$this->do_stats( 'server_side' );
627
628
			wp_send_json_success();
629
		}
630
	}
631
632
	/**
633
	 * If there are any stats that need to be pushed, but haven't been, push them now.
634
	 */
635
	function __destruct() {
636
		if ( ! empty( $this->stats ) ) {
637
			$this->do_stats( 'server_side' );
638
		}
639
	}
640
641
	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...
642
		switch( $cap ) {
643
			case 'jetpack_connect' :
644
			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...
645
				if ( Jetpack::is_development_mode() ) {
646
					$caps = array( 'do_not_allow' );
647
					break;
648
				}
649
				/**
650
				 * Pass through. If it's not development mode, these should match disconnect.
651
				 * Let users disconnect if it's development mode, just in case things glitch.
652
				 */
653
			case 'jetpack_disconnect' :
654
				/**
655
				 * In multisite, can individual site admins manage their own connection?
656
				 *
657
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
658
				 */
659
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
660
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
661
						/**
662
						 * We need to update the option name -- it's terribly unclear which
663
						 * direction the override goes.
664
						 *
665
						 * @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...
666
						 */
667
						$caps = array( 'do_not_allow' );
668
						break;
669
					}
670
				}
671
672
				$caps = array( 'manage_options' );
673
				break;
674
			case 'jetpack_manage_modules' :
675
			case 'jetpack_activate_modules' :
676
			case 'jetpack_deactivate_modules' :
677
				$caps = array( 'manage_options' );
678
				break;
679
			case 'jetpack_configure_modules' :
680
				$caps = array( 'manage_options' );
681
				break;
682
			case 'jetpack_network_admin_page':
683
			case 'jetpack_network_settings_page':
684
				$caps = array( 'manage_network_plugins' );
685
				break;
686
			case 'jetpack_network_sites_page':
687
				$caps = array( 'manage_sites' );
688
				break;
689
			case 'jetpack_admin_page' :
690
				if ( Jetpack::is_development_mode() ) {
691
					$caps = array( 'manage_options' );
692
					break;
693
				} else {
694
					$caps = array( 'read' );
695
				}
696
				break;
697
			case 'jetpack_connect_user' :
698
				if ( Jetpack::is_development_mode() ) {
699
					$caps = array( 'do_not_allow' );
700
					break;
701
				}
702
				$caps = array( 'read' );
703
				break;
704
		}
705
		return $caps;
706
	}
707
708
	function require_jetpack_authentication() {
709
		// Don't let anyone authenticate
710
		$_COOKIE = array();
711
		remove_all_filters( 'authenticate' );
712
713
		/**
714
		 * For the moment, remove Limit Login Attempts if its xmlrpc for Jetpack.
715
		 * If Limit Login Attempts is installed as a mu-plugin, it can occasionally
716
		 * generate false-positives.
717
		 */
718
		remove_filter( 'wp_login_failed', 'limit_login_failed' );
719
720
		if ( Jetpack::is_active() ) {
721
			// Allow Jetpack authentication
722
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
723
		}
724
	}
725
726
	/**
727
	 * Load language files
728
	 * @action plugins_loaded
729
	 */
730
	public static function plugin_textdomain() {
731
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
732
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
733
	}
734
735
	/**
736
	 * Register assets for use in various modules and the Jetpack admin page.
737
	 *
738
	 * @uses wp_script_is, wp_register_script, plugins_url
739
	 * @action wp_loaded
740
	 * @return null
741
	 */
742
	public function register_assets() {
743
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
744
			wp_register_script( 'spin', plugins_url( '_inc/spin.js', JETPACK__PLUGIN_FILE ), false, '1.3' );
745
		}
746
747 View Code Duplication
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
748
			wp_register_script( 'jquery.spin', plugins_url( '_inc/jquery.spin.js', JETPACK__PLUGIN_FILE ) , array( 'jquery', 'spin' ), '1.3' );
749
		}
750
751 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
752
			wp_register_script( 'jetpack-gallery-settings', plugins_url( '_inc/gallery-settings.js', JETPACK__PLUGIN_FILE ), array( 'media-views' ), '20121225' );
753
		}
754
755 View Code Duplication
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
756
			wp_register_script( 'jetpack-twitter-timeline', plugins_url( '_inc/twitter-timeline.js', JETPACK__PLUGIN_FILE ) , array( 'jquery' ), '4.0.0', true );
757
		}
758
759
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
760
			wp_register_script( 'jetpack-facebook-embed', plugins_url( '_inc/facebook-embed.js', __FILE__ ), array( 'jquery' ), null, true );
761
762
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
763
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
764
			if ( ! is_numeric( $fb_app_id ) ) {
765
				$fb_app_id = '';
766
			}
767
			wp_localize_script(
768
				'jetpack-facebook-embed',
769
				'jpfbembed',
770
				array(
771
					'appid' => $fb_app_id,
772
					'locale' => $this->get_locale(),
773
				)
774
			);
775
		}
776
777
		/**
778
		 * As jetpack_register_genericons is by default fired off a hook,
779
		 * the hook may have already fired by this point.
780
		 * So, let's just trigger it manually.
781
		 */
782
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
783
		jetpack_register_genericons();
784
785 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
786
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
787
	}
788
789
	/**
790
	 * Guess locale from language code.
791
	 *
792
	 * @param string $lang Language code.
793
	 * @return string|bool
794
	 */
795 View Code Duplication
	function guess_locale_from_lang( $lang ) {
796
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
797
			return 'en_US';
798
		}
799
800
		if ( ! class_exists( 'GP_Locales' ) ) {
801
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
802
				return false;
803
			}
804
805
			require JETPACK__GLOTPRESS_LOCALES_PATH;
806
		}
807
808
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
809
			// WP.com: get_locale() returns 'it'
810
			$locale = GP_Locales::by_slug( $lang );
811
		} else {
812
			// Jetpack: get_locale() returns 'it_IT';
813
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
814
		}
815
816
		if ( ! $locale ) {
817
			return false;
818
		}
819
820
		if ( empty( $locale->facebook_locale ) ) {
821
			if ( empty( $locale->wp_locale ) ) {
822
				return false;
823
			} else {
824
				// Facebook SDK is smart enough to fall back to en_US if a
825
				// locale isn't supported. Since supported Facebook locales
826
				// can fall out of sync, we'll attempt to use the known
827
				// wp_locale value and rely on said fallback.
828
				return $locale->wp_locale;
829
			}
830
		}
831
832
		return $locale->facebook_locale;
833
	}
834
835
	/**
836
	 * Get the locale.
837
	 *
838
	 * @return string|bool
839
	 */
840
	function get_locale() {
841
		$locale = $this->guess_locale_from_lang( get_locale() );
842
843
		if ( ! $locale ) {
844
			$locale = 'en_US';
845
		}
846
847
		return $locale;
848
	}
849
850
	/**
851
	 * Device Pixels support
852
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
853
	 */
854
	function devicepx() {
855
		if ( Jetpack::is_active() ) {
856
			wp_enqueue_script( 'devicepx', set_url_scheme( 'http://s0.wp.com/wp-content/js/devicepx-jetpack.js' ), array(), gmdate( 'oW' ), true );
857
		}
858
	}
859
860
	/**
861
	 * Return the network_site_url so that .com knows what network this site is a part of.
862
	 * @param  bool $option
863
	 * @return string
864
	 */
865
	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...
866
		return network_site_url();
867
	}
868
	/**
869
	 * Network Name.
870
	 */
871
	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...
872
		global $current_site;
873
		return $current_site->site_name;
874
	}
875
	/**
876
	 * Does the network allow new user and site registrations.
877
	 * @return string
878
	 */
879
	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...
880
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
881
	}
882
	/**
883
	 * Does the network allow admins to add new users.
884
	 * @return boolian
885
	 */
886
	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...
887
		return (bool) get_site_option( 'add_new_users' );
888
	}
889
	/**
890
	 * File upload psace left per site in MB.
891
	 *  -1 means NO LIMIT.
892
	 * @return number
893
	 */
894
	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...
895
		// value in MB
896
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
897
	}
898
899
	/**
900
	 * Network allowed file types.
901
	 * @return string
902
	 */
903
	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...
904
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
905
	}
906
907
	/**
908
	 * Maximum file upload size set by the network.
909
	 * @return number
910
	 */
911
	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...
912
		// value in KB
913
		return get_site_option( 'fileupload_maxk', 300 );
914
	}
915
916
	/**
917
	 * Lets us know if a site allows admins to manage the network.
918
	 * @return array
919
	 */
920
	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...
921
		return get_site_option( 'menu_items' );
922
	}
923
924
	/**
925
	 * Return whether we are dealing with a multi network setup or not.
926
	 * The reason we are type casting this is because we want to avoid the situation where
927
	 * the result is false since when is_main_network_option return false it cases
928
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
929
	 * database which could be set to anything as opposed to what this function returns.
930
	 * @param  bool  $option
931
	 *
932
	 * @return boolean
933
	 */
934
	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...
935
		// return '1' or ''
936
		return (string) (bool) Jetpack::is_multi_network();
937
	}
938
939
	/**
940
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
941
	 *
942
	 * @param  string  $option
943
	 * @return boolean
944
	 */
945
	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...
946
		return (string) (bool) is_multisite();
947
	}
948
949
	/**
950
	 * Implemented since there is no core is multi network function
951
	 * Right now there is no way to tell if we which network is the dominant network on the system
952
	 *
953
	 * @since  3.3
954
	 * @return boolean
955
	 */
956
	public static function is_multi_network() {
957
		global  $wpdb;
958
959
		// if we don't have a multi site setup no need to do any more
960
		if ( ! is_multisite() ) {
961
			return false;
962
		}
963
964
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
965
		if ( $num_sites > 1 ) {
966
			return true;
967
		} else {
968
			return false;
969
		}
970
	}
971
972
	/**
973
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
974
	 * @return null
975
	 */
976
	function update_jetpack_main_network_site_option() {
977
		// do_action( 'add_option_$option', '$option', '$value-of-the-option' );
978
		/**
979
		 * Fires when the site URL is updated.
980
		 * Determines if the site is the main site of a Mulitiste network.
981
		 *
982
		 * @since 3.3.0
983
		 *
984
		 * @param string jetpack_main_network_site.
985
		 * @param string network_site_url() Site URL for the "main" site of the current Multisite network.
986
		 */
987
		do_action( 'add_option_jetpack_main_network_site', 'jetpack_main_network_site', network_site_url() );
988
		/**
989
		 * Fires when the site URL is updated.
990
		 * Determines if the is part of a multi network.
991
		 *
992
		 * @since 3.3.0
993
		 *
994
		 * @param string jetpack_is_main_network.
995
		 * @param bool Jetpack::is_multi_network() Is the site part of a multi network.
996
		 */
997
		do_action( 'add_option_jetpack_is_main_network', 'jetpack_is_main_network', (string) (bool) Jetpack::is_multi_network() );
998
		/**
999
		 * Fires when the site URL is updated.
1000
		 * Determines if the site is part of a multisite network.
1001
		 *
1002
		 * @since 3.4.0
1003
		 *
1004
		 * @param string jetpack_is_multi_site.
1005
		 * @param bool is_multisite() Is the site part of a mutlisite network.
1006
		 */
1007
		do_action( 'add_option_jetpack_is_multi_site', 'jetpack_is_multi_site', (string) (bool) is_multisite() );
1008
	}
1009
	/**
1010
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1011
	 *
1012
	 */
1013
	function update_jetpack_network_settings() {
1014
		// Only sync this info for the main network site.
1015
		do_action( 'add_option_jetpack_network_name', 'jetpack_network_name', Jetpack::network_name() );
1016
		do_action( 'add_option_jetpack_network_allow_new_registrations', 'jetpack_network_allow_new_registrations', Jetpack::network_allow_new_registrations() );
1017
		do_action( 'add_option_jetpack_network_add_new_users', 'jetpack_network_add_new_users', Jetpack::network_add_new_users() );
1018
		do_action( 'add_option_jetpack_network_site_upload_space', 'jetpack_network_site_upload_space', Jetpack::network_site_upload_space() );
1019
		do_action( 'add_option_jetpack_network_upload_file_types', 'jetpack_network_upload_file_types', Jetpack::network_upload_file_types() );
1020
		do_action( 'add_option_jetpack_network_enable_administration_menus', 'jetpack_network_enable_administration_menus', Jetpack::network_enable_administration_menus() );
1021
1022
	}
1023
1024
	/**
1025
	 * Get back if the current site is single user site.
1026
	 *
1027
	 * @return bool
1028
	 */
1029
	public static function is_single_user_site() {
1030
1031
		$user_query = new WP_User_Query( array(
1032
			'blog_id' => get_current_blog_id(),
1033
			'fields'  => 'ID',
1034
			'number' => 2
1035
		) );
1036
		return 1 === (int) $user_query->get_total();
1037
	}
1038
1039
	/**
1040
	 * Returns true if the site has file write access false otherwise.
1041
	 * @return string ( '1' | '0' )
1042
	 **/
1043
	public static function file_system_write_access() {
1044
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1045
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
1046
		}
1047
1048
		require_once( ABSPATH . 'wp-admin/includes/template.php' );
1049
1050
		$filesystem_method = get_filesystem_method();
1051
		if ( $filesystem_method === 'direct' ) {
1052
			return 1;
1053
		}
1054
1055
		ob_start();
1056
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1057
		ob_end_clean();
1058
		if ( $filesystem_credentials_are_stored ) {
1059
			return 1;
1060
		}
1061
		return 0;
1062
	}
1063
1064
	/**
1065
	 * Finds out if a site is using a version control system.
1066
	 * @return string ( '1' | '0' )
1067
	 **/
1068
	public static function is_version_controlled() {
1069
		_deprecated_function( 'Jetpack::is_version_controlled', '4.1', 'Jetpack_Sync_Functions::is_version_controlled' );
1070
		return (string) (int) Jetpack_Sync_Functions::is_version_controlled();
1071
	}
1072
1073
	/**
1074
	 * Determines whether the current theme supports featured images or not.
1075
	 * @return string ( '1' | '0' )
1076
	 */
1077
	public static function featured_images_enabled() {
1078
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1079
	}
1080
1081
	/*
1082
	 * Sync back wp_version
1083
	 */
1084
	public static function get_wp_version() {
1085
		global $wp_version;
1086
		return $wp_version;
1087
	}
1088
1089
	/**
1090
	 * Keeps wp_version in sync with .com when WordPress core updates
1091
	 **/
1092
	public static function update_get_wp_version( $update, $meta_data ) {
1093
		if ( 'update' === $meta_data['action'] && 'core' === $meta_data['type'] ) {
1094
			/** This action is documented in wp-includes/option.php */
1095
			/**
1096
			 * This triggers the sync for the jetpack version
1097
			 * See Jetpack_Sync options method for more info.
1098
			 */
1099
			do_action( 'add_option_jetpack_wp_version', 'jetpack_wp_version', (string) Jetpack::get_wp_version() );
1100
		}
1101
	}
1102
1103
	/**
1104
	 * jetpack_updates is saved in the following schema:
1105
	 *
1106
	 * array (
1107
	 *      'plugins'                       => (int) Number of plugin updates available.
1108
	 *      'themes'                        => (int) Number of theme updates available.
1109
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1110
	 *      'translations'                  => (int) Number of translation updates available.
1111
	 *      'total'                         => (int) Total of all available updates.
1112
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1113
	 * )
1114
	 * @return array
1115
	 */
1116
	public static function get_updates() {
1117
		$update_data = wp_get_update_data();
1118
1119
		// Stores the individual update counts as well as the total count.
1120
		if ( isset( $update_data['counts'] ) ) {
1121
			$updates = $update_data['counts'];
1122
		}
1123
1124
		// If we need to update WordPress core, let's find the latest version number.
1125 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1126
			$cur = get_preferred_from_update_core();
1127
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1128
				$updates['wp_update_version'] = $cur->current;
1129
			}
1130
		}
1131
		return isset( $updates ) ? $updates : array();
1132
	}
1133
1134
	public static function get_update_details() {
1135
		$update_details = array(
1136
			'update_core' => get_site_transient( 'update_core' ),
1137
			'update_plugins' => get_site_transient( 'update_plugins' ),
1138
			'update_themes' => get_site_transient( 'update_themes' ),
1139
		);
1140
		return $update_details;
1141
	}
1142
1143
	public static function refresh_update_data() {
1144
		if ( current_user_can( 'update_core' ) && current_user_can( 'update_plugins' ) && current_user_can( 'update_themes' ) ) {
1145
			/**
1146
			 * Fires whenever the amount of updates needed for a site changes.
1147
			 * Syncs an array that includes the number of theme, plugin, and core updates available, as well as the latest core version available.
1148
			 *
1149
			 * @since 3.7.0
1150
			 *
1151
			 * @param string jetpack_updates
1152
			 * @param array Update counts calculated by Jetpack::get_updates
1153
			 */
1154
			do_action( 'add_option_jetpack_updates', 'jetpack_updates', Jetpack::get_updates() );
1155
		}
1156
		/**
1157
		 * Fires whenever the amount of updates needed for a site changes.
1158
		 * Syncs an array of core, theme, and plugin data, and which of each is out of date
1159
		 *
1160
		 * @since 3.7.0
1161
		 *
1162
		 * @param string jetpack_update_details
1163
		 * @param array Update details calculated by Jetpack::get_update_details
1164
		 */
1165
		do_action( 'add_option_jetpack_update_details', 'jetpack_update_details', Jetpack::get_update_details() );
1166
	}
1167
1168
	public static function refresh_theme_data() {
1169
		/**
1170
		 * Fires whenever a theme change is made.
1171
		 *
1172
		 * @since 3.8.1
1173
		 *
1174
		 * @param string featured_images_enabled
1175
		 * @param boolean Whether featured images are enabled or not
1176
		 */
1177
		do_action( 'add_option_jetpack_featured_images_enabled', 'jetpack_featured_images_enabled', Jetpack::featured_images_enabled() );
1178
	}
1179
1180
	/**
1181
	 * Is Jetpack active?
1182
	 */
1183
	public static function is_active() {
1184
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1185
	}
1186
1187
	/**
1188
	 * Is Jetpack in development (offline) mode?
1189
	 */
1190
	public static function is_development_mode() {
1191
		$development_mode = false;
1192
1193
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1194
			$development_mode = JETPACK_DEV_DEBUG;
1195
		}
1196
1197
		elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1198
			$development_mode = true;
1199
		}
1200
		/**
1201
		 * Filters Jetpack's development mode.
1202
		 *
1203
		 * @see https://jetpack.com/support/development-mode/
1204
		 *
1205
		 * @since 2.2.1
1206
		 *
1207
		 * @param bool $development_mode Is Jetpack's development mode active.
1208
		 */
1209
		return apply_filters( 'jetpack_development_mode', $development_mode );
1210
	}
1211
1212
	/**
1213
	* Get Jetpack development mode notice text and notice class.
1214
	*
1215
	* Mirrors the checks made in Jetpack::is_development_mode
1216
	*
1217
	*/
1218
	public static function show_development_mode_notice() {
1219
		if ( Jetpack::is_development_mode() ) {
1220
			if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1221
				$notice = sprintf(
1222
					/* translators: %s is a URL */
1223
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the JETPACK_DEV_DEBUG constant being defined in wp-config.php or elsewhere.', 'jetpack' ),
1224
					'https://jetpack.com/support/development-mode/'
1225
				);
1226
			} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1227
				$notice = sprintf(
1228
					/* translators: %s is a URL */
1229
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via site URL lacking a dot (e.g. http://localhost).', 'jetpack' ),
1230
					'https://jetpack.com/support/development-mode/'
1231
				);
1232
			} else {
1233
				$notice = sprintf(
1234
					/* translators: %s is a URL */
1235
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the jetpack_development_mode filter.', 'jetpack' ),
1236
					'https://jetpack.com/support/development-mode/'
1237
				);
1238
			}
1239
1240
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1241
		}
1242
1243
		// Throw up a notice if using a development version and as for feedback.
1244
		if ( Jetpack::is_development_version() ) {
1245
			/* translators: %s is a URL */
1246
			$notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), 'https://jetpack.com/contact-support/beta-group/' );
1247
1248
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1249
		}
1250
		// Throw up a notice if using staging mode
1251
		if ( Jetpack::is_staging_site() ) {
1252
			/* translators: %s is a URL */
1253
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.com/support/staging-sites/' );
1254
1255
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1256
		}
1257
	}
1258
1259
	/**
1260
	 * Whether Jetpack's version maps to a public release, or a development version.
1261
	 */
1262
	public static function is_development_version() {
1263
		return ! preg_match( '/^\d+(\.\d+)+$/', JETPACK__VERSION );
1264
	}
1265
1266
	/**
1267
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1268
	 */
1269
	public static function is_user_connected( $user_id = false ) {
1270
		$user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
1271
		if ( ! $user_id ) {
1272
			return false;
1273
		}
1274
		return (bool) Jetpack_Data::get_access_token( $user_id );
1275
	}
1276
1277
	/**
1278
	 * Get the wpcom user data of the current|specified connected user.
1279
	 */
1280 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1281
		if ( ! $user_id ) {
1282
			$user_id = get_current_user_id();
1283
		}
1284
		Jetpack::load_xml_rpc_client();
1285
		$xml = new Jetpack_IXR_Client( array(
1286
			'user_id' => $user_id,
1287
		) );
1288
		$xml->query( 'wpcom.getUser' );
1289
		if ( ! $xml->isError() ) {
1290
			return $xml->getResponse();
1291
		}
1292
		return false;
1293
	}
1294
1295
	/**
1296
	 * Get the wpcom email of the current|specified connected user.
1297
	 */
1298 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1299
		if ( ! $user_id ) {
1300
			$user_id = get_current_user_id();
1301
		}
1302
		Jetpack::load_xml_rpc_client();
1303
		$xml = new Jetpack_IXR_Client( array(
1304
			'user_id' => $user_id,
1305
		) );
1306
		$xml->query( 'wpcom.getUserEmail' );
1307
		if ( ! $xml->isError() ) {
1308
			return $xml->getResponse();
1309
		}
1310
		return false;
1311
	}
1312
1313
	/**
1314
	 * Get the wpcom email of the master user.
1315
	 */
1316
	public static function get_master_user_email() {
1317
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1318
		if ( $master_user_id ) {
1319
			return self::get_connected_user_email( $master_user_id );
1320
		}
1321
		return '';
1322
	}
1323
1324
	function current_user_is_connection_owner() {
1325
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1326
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1327
	}
1328
1329
	/**
1330
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1331
	 */
1332
	function extra_oembed_providers() {
1333
		// Cloudup: https://dev.cloudup.com/#oembed
1334
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1335
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1336
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1337
		wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true );
1338
		wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true );
1339
	}
1340
1341
	/**
1342
	 * Synchronize connected user role changes
1343
	 */
1344
	function user_role_change( $user_id ) {
1345
		if ( Jetpack::is_active() && Jetpack::is_user_connected( $user_id ) ) {
1346
			$current_user_id = get_current_user_id();
1347
			wp_set_current_user( $user_id );
1348
			$role = $this->translate_current_user_to_role();
1349
			$signed_role = $this->sign_role( $role );
1350
			wp_set_current_user( $current_user_id );
1351
1352
			$master_token   = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1353
			$master_user_id = absint( $master_token->external_user_id );
1354
1355
			if ( ! $master_user_id )
1356
				return; // this shouldn't happen
1357
1358
			Jetpack::xmlrpc_async_call( 'jetpack.updateRole', $user_id, $signed_role );
1359
			//@todo retry on failure
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1360
1361
			//try to choose a new master if we're demoting the current one
1362 View Code Duplication
			if ( $user_id == $master_user_id && 'administrator' != $role ) {
1363
				$query = new WP_User_Query(
1364
					array(
1365
						'fields'  => array( 'id' ),
1366
						'role'    => 'administrator',
1367
						'orderby' => 'id',
1368
						'exclude' => array( $master_user_id ),
1369
					)
1370
				);
1371
				$new_master = false;
1372
				foreach ( $query->results as $result ) {
1373
					$uid = absint( $result->id );
1374
					if ( $uid && Jetpack::is_user_connected( $uid ) ) {
1375
						$new_master = $uid;
1376
						break;
1377
					}
1378
				}
1379
1380
				if ( $new_master ) {
1381
					Jetpack_Options::update_option( 'master_user', $new_master );
1382
				}
1383
				// else disconnect..?
1384
			}
1385
		}
1386
	}
1387
1388
	/**
1389
	 * Loads the currently active modules.
1390
	 */
1391
	public static function load_modules() {
1392
		if ( ! self::is_active() && !self::is_development_mode() ) {
1393
			if ( ! is_multisite() || ! get_site_option( 'jetpack_protect_active' ) ) {
1394
				return;
1395
			}
1396
		}
1397
1398
		$version = Jetpack_Options::get_option( 'version' );
1399 View Code Duplication
		if ( ! $version ) {
1400
			$version = $old_version = JETPACK__VERSION . ':' . time();
1401
			/** This action is documented in class.jetpack.php */
1402
			do_action( 'updating_jetpack_version', $version, false );
1403
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1404
		}
1405
		list( $version ) = explode( ':', $version );
1406
1407
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1408
1409
		$modules_data = array();
1410
1411
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1412
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1413
			$updated_modules = array();
1414
			foreach ( $modules as $module ) {
1415
				$modules_data[ $module ] = Jetpack::get_module( $module );
1416
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1417
					continue;
1418
				}
1419
1420
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1421
					continue;
1422
				}
1423
1424
				$updated_modules[] = $module;
1425
			}
1426
1427
			$modules = array_diff( $modules, $updated_modules );
1428
		}
1429
1430
		$is_development_mode = Jetpack::is_development_mode();
1431
1432
		foreach ( $modules as $index => $module ) {
1433
			// If we're in dev mode, disable modules requiring a connection
1434
			if ( $is_development_mode ) {
1435
				// Prime the pump if we need to
1436
				if ( empty( $modules_data[ $module ] ) ) {
1437
					$modules_data[ $module ] = Jetpack::get_module( $module );
1438
				}
1439
				// If the module requires a connection, but we're in local mode, don't include it.
1440
				if ( $modules_data[ $module ]['requires_connection'] ) {
1441
					continue;
1442
				}
1443
			}
1444
1445
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1446
				continue;
1447
			}
1448
1449
			if ( ! @include( Jetpack::get_module_path( $module ) ) ) {
1450
				unset( $modules[ $index ] );
1451
				Jetpack_Options::update_option( 'active_modules', array_values( $modules ) );
1452
				continue;
1453
			}
1454
1455
			/**
1456
			 * Fires when a specific module is loaded.
1457
			 * The dynamic part of the hook, $module, is the module slug.
1458
			 *
1459
			 * @since 1.1.0
1460
			 */
1461
			do_action( 'jetpack_module_loaded_' . $module );
1462
		}
1463
1464
		/**
1465
		 * Fires when all the modules are loaded.
1466
		 *
1467
		 * @since 1.1.0
1468
		 */
1469
		do_action( 'jetpack_modules_loaded' );
1470
1471
		// 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.
1472
		if ( Jetpack::is_active() || Jetpack::is_development_mode() )
1473
			require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
1474
	}
1475
1476
	/**
1477
	 * Check if Jetpack's REST API compat file should be included
1478
	 * @action plugins_loaded
1479
	 * @return null
1480
	 */
1481
	public function check_rest_api_compat() {
1482
		/**
1483
		 * Filters the list of REST API compat files to be included.
1484
		 *
1485
		 * @since 2.2.5
1486
		 *
1487
		 * @param array $args Array of REST API compat files to include.
1488
		 */
1489
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1490
1491
		if ( function_exists( 'bbpress' ) )
1492
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
1493
1494
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
1495
			require_once $_jetpack_rest_api_compat_include;
1496
	}
1497
1498
	/**
1499
	 * Gets all plugins currently active in values, regardless of whether they're
1500
	 * traditionally activated or network activated.
1501
	 *
1502
	 * @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...
1503
	 */
1504
	public static function get_active_plugins() {
1505
		$active_plugins = (array) get_option( 'active_plugins', array() );
1506
1507
		if ( is_multisite() ) {
1508
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1509
			// whereas active_plugins stores them in the values.
1510
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1511
			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...
1512
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1513
			}
1514
		}
1515
1516
		sort( $active_plugins );
1517
1518
		return array_unique( $active_plugins );
1519
	}
1520
1521
	/**
1522
	 * Gets and parses additional plugin data to send with the heartbeat data
1523
	 *
1524
	 * @since 3.8.1
1525
	 *
1526
	 * @return array Array of plugin data
1527
	 */
1528
	public static function get_parsed_plugin_data() {
1529
		if ( ! function_exists( 'get_plugins' ) ) {
1530
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1531
		}
1532
		$all_plugins    = get_plugins();
1533
		$active_plugins = Jetpack::get_active_plugins();
1534
1535
		$plugins = array();
1536
		foreach ( $all_plugins as $path => $plugin_data ) {
1537
			$plugins[ $path ] = array(
1538
					'is_active' => in_array( $path, $active_plugins ),
1539
					'file'      => $path,
1540
					'name'      => $plugin_data['Name'],
1541
					'version'   => $plugin_data['Version'],
1542
					'author'    => $plugin_data['Author'],
1543
			);
1544
		}
1545
1546
		return $plugins;
1547
	}
1548
1549
	/**
1550
	 * Gets and parses theme data to send with the heartbeat data
1551
	 *
1552
	 * @since 3.8.1
1553
	 *
1554
	 * @return array Array of theme data
1555
	 */
1556
	public static function get_parsed_theme_data() {
1557
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
1558
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
1559
1560
		$themes = array();
1561
		foreach ( $all_themes as $slug => $theme_data ) {
1562
			$theme_headers = array();
1563
			foreach ( $header_keys as $header_key ) {
1564
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
1565
			}
1566
1567
			$themes[ $slug ] = array(
1568
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
1569
					'slug' => $slug,
1570
					'theme_root' => $theme_data->get_theme_root_uri(),
1571
					'parent' => $theme_data->parent(),
1572
					'headers' => $theme_headers
1573
			);
1574
		}
1575
1576
		return $themes;
1577
	}
1578
1579
	/**
1580
	 * Checks whether a specific plugin is active.
1581
	 *
1582
	 * We don't want to store these in a static variable, in case
1583
	 * there are switch_to_blog() calls involved.
1584
	 */
1585
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
1586
		return in_array( $plugin, self::get_active_plugins() );
1587
	}
1588
1589
	/**
1590
	 * Check if Jetpack's Open Graph tags should be used.
1591
	 * If certain plugins are active, Jetpack's og tags are suppressed.
1592
	 *
1593
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1594
	 * @action plugins_loaded
1595
	 * @return null
1596
	 */
1597
	public function check_open_graph() {
1598
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
1599
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
1600
		}
1601
1602
		$active_plugins = self::get_active_plugins();
1603
1604
		if ( ! empty( $active_plugins ) ) {
1605
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
1606
				if ( in_array( $plugin, $active_plugins ) ) {
1607
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
1608
					break;
1609
				}
1610
			}
1611
		}
1612
1613
		/**
1614
		 * Allow the addition of Open Graph Meta Tags to all pages.
1615
		 *
1616
		 * @since 2.0.3
1617
		 *
1618
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
1619
		 */
1620
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
1621
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
1622
		}
1623
	}
1624
1625
	/**
1626
	 * Check if Jetpack's Twitter tags should be used.
1627
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
1628
	 *
1629
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
1630
	 * @action plugins_loaded
1631
	 * @return null
1632
	 */
1633
	public function check_twitter_tags() {
1634
1635
		$active_plugins = self::get_active_plugins();
1636
1637
		if ( ! empty( $active_plugins ) ) {
1638
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
1639
				if ( in_array( $plugin, $active_plugins ) ) {
1640
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
1641
					break;
1642
				}
1643
			}
1644
		}
1645
1646
		/**
1647
		 * Allow Twitter Card Meta tags to be disabled.
1648
		 *
1649
		 * @since 2.6.0
1650
		 *
1651
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
1652
		 */
1653
		if ( apply_filters( 'jetpack_disable_twitter_cards', true ) ) {
1654
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
1655
		}
1656
	}
1657
1658
1659
1660
1661
	/*
1662
	 *
1663
	 * Jetpack Security Reports
1664
	 *
1665
	 * Allowed types: login_form, backup, file_scanning, spam
1666
	 *
1667
	 * 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)
1668
	 *
1669
	 * 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)
1670
	 *
1671
	 *
1672
	 * Example code to submit a security report:
1673
	 *
1674
	 *  function akismet_submit_jetpack_security_report() {
1675
	 *  	Jetpack::submit_security_report( 'spam', __FILE__, $args = array( 'blocked' => 138284, status => 'ok' ) );
1676
	 *  }
1677
	 *  add_action( 'jetpack_security_report', 'akismet_submit_jetpack_security_report' );
1678
	 *
1679
	 */
1680
1681
1682
	/**
1683
	 * Calls for security report submissions.
1684
	 *
1685
	 * @return null
1686
	 */
1687
	public static function perform_security_reporting() {
1688
		$no_check_needed = get_site_transient( 'security_report_performed_recently' );
1689
1690
		if ( $no_check_needed ) {
1691
			return;
1692
		}
1693
1694
		/**
1695
		 * Fires before a security report is created.
1696
		 *
1697
		 * @since 3.4.0
1698
		 */
1699
		do_action( 'jetpack_security_report' );
1700
1701
		Jetpack_Options::update_option( 'security_report', self::$security_report );
1702
		set_site_transient( 'security_report_performed_recently', 1, 15 * MINUTE_IN_SECONDS );
1703
	}
1704
1705
	/**
1706
	 * Allows plugins to submit security reports.
1707
 	 *
1708
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
1709
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
1710
	 * @param array   $args         See definitions above
1711
	 */
1712
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
1713
1714
		if( !doing_action( 'jetpack_security_report' ) ) {
1715
			return new WP_Error( 'not_collecting_report', 'Not currently collecting security reports.  Please use the jetpack_security_report hook.' );
1716
		}
1717
1718
		if( !is_string( $type ) || !is_string( $plugin_file ) ) {
1719
			return new WP_Error( 'invalid_security_report', 'Invalid Security Report' );
1720
		}
1721
1722
		if( !function_exists( 'get_plugin_data' ) ) {
1723
			include( ABSPATH . 'wp-admin/includes/plugin.php' );
1724
		}
1725
1726
		//Get rid of any non-allowed args
1727
		$args = array_intersect_key( $args, array_flip( array( 'blocked', 'last', 'next', 'status', 'message' ) ) );
1728
1729
		$plugin = get_plugin_data( $plugin_file );
1730
1731
		if ( !$plugin['Name'] ) {
1732
			return new WP_Error( 'security_report_missing_plugin_name', 'Invalid Plugin File Provided' );
1733
		}
1734
1735
		// Sanitize everything to make sure we're not syncing something wonky
1736
		$type = sanitize_key( $type );
1737
1738
		$args['plugin'] = $plugin;
1739
1740
		// Cast blocked, last and next as integers.
1741
		// Last and next should be in unix timestamp format
1742
		if ( isset( $args['blocked'] ) ) {
1743
			$args['blocked'] = (int) $args['blocked'];
1744
		}
1745
		if ( isset( $args['last'] ) ) {
1746
			$args['last'] = (int) $args['last'];
1747
		}
1748
		if ( isset( $args['next'] ) ) {
1749
			$args['next'] = (int) $args['next'];
1750
		}
1751
		if ( !in_array( $args['status'], array( 'ok', 'warning', 'error' ) ) ) {
1752
			$args['status'] = 'ok';
1753
		}
1754
		if ( isset( $args['message'] ) ) {
1755
1756
			if( $args['status'] == 'ok' ) {
1757
				unset( $args['message'] );
1758
			}
1759
1760
			$allowed_html = array(
1761
			    'a' => array(
1762
			        'href' => array(),
1763
			        'title' => array()
1764
			    ),
1765
			    'em' => array(),
1766
			    'strong' => array(),
1767
			);
1768
1769
			$args['message'] = wp_kses( $args['message'], $allowed_html );
1770
		}
1771
1772
		$plugin_name = $plugin[ 'Name' ];
1773
1774
		self::$security_report[ $type ][ $plugin_name ] = $args;
1775
	}
1776
1777
	/**
1778
	 * Collects a new report if needed, then returns it.
1779
	 */
1780
	public function get_security_report() {
1781
		self::perform_security_reporting();
1782
		return Jetpack_Options::get_option( 'security_report' );
1783
	}
1784
1785
1786
/* Jetpack Options API */
1787
1788
	public static function get_option_names( $type = 'compact' ) {
1789
		return Jetpack_Options::get_option_names( $type );
1790
	}
1791
1792
	/**
1793
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
1794
 	 *
1795
	 * @param string $name    Option name
1796
	 * @param mixed  $default (optional)
1797
	 */
1798
	public static function get_option( $name, $default = false ) {
1799
		return Jetpack_Options::get_option( $name, $default );
1800
	}
1801
1802
	/**
1803
	* Stores two secrets and a timestamp so WordPress.com can make a request back and verify an action
1804
	* Does some extra verification so urls (such as those to public-api, register, etc) can't just be crafted
1805
	* $name must be a registered option name.
1806
	*/
1807
	public static function create_nonce( $name ) {
1808
		$secret = wp_generate_password( 32, false ) . ':' . wp_generate_password( 32, false ) . ':' . ( time() + 600 );
1809
1810
		Jetpack_Options::update_option( $name, $secret );
1811
		@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...
1812
		if ( empty( $secret_1 ) || empty( $secret_2 ) || $eol < time() )
1813
			return new Jetpack_Error( 'missing_secrets' );
1814
1815
		return array(
1816
			'secret_1' => $secret_1,
1817
			'secret_2' => $secret_2,
1818
			'eol'      => $eol,
1819
		);
1820
	}
1821
1822
	/**
1823
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
1824
 	 *
1825
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
1826
	 * @param string $name  Option name
1827
	 * @param mixed  $value Option value
1828
	 */
1829
	public static function update_option( $name, $value ) {
1830
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
1831
		return Jetpack_Options::update_option( $name, $value );
1832
	}
1833
1834
	/**
1835
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
1836
 	 *
1837
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
1838
	 * @param array $array array( option name => option value, ... )
1839
	 */
1840
	public static function update_options( $array ) {
1841
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
1842
		return Jetpack_Options::update_options( $array );
1843
	}
1844
1845
	/**
1846
	 * Deletes the given option.  May be passed multiple option names as an array.
1847
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
1848
	 *
1849
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
1850
	 * @param string|array $names
1851
	 */
1852
	public static function delete_option( $names ) {
1853
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
1854
		return Jetpack_Options::delete_option( $names );
1855
	}
1856
1857
	/**
1858
	 * Enters a user token into the user_tokens option
1859
	 *
1860
	 * @param int $user_id
1861
	 * @param string $token
1862
	 * return bool
1863
	 */
1864
	public static function update_user_token( $user_id, $token, $is_master_user ) {
1865
		// not designed for concurrent updates
1866
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
1867
		if ( ! is_array( $user_tokens ) )
1868
			$user_tokens = array();
1869
		$user_tokens[$user_id] = $token;
1870
		if ( $is_master_user ) {
1871
			$master_user = $user_id;
1872
			$options     = compact( 'user_tokens', 'master_user' );
1873
		} else {
1874
			$options = compact( 'user_tokens' );
1875
		}
1876
		return Jetpack_Options::update_options( $options );
1877
	}
1878
1879
	/**
1880
	 * Returns an array of all PHP files in the specified absolute path.
1881
	 * Equivalent to glob( "$absolute_path/*.php" ).
1882
	 *
1883
	 * @param string $absolute_path The absolute path of the directory to search.
1884
	 * @return array Array of absolute paths to the PHP files.
1885
	 */
1886
	public static function glob_php( $absolute_path ) {
1887
		if ( function_exists( 'glob' ) ) {
1888
			return glob( "$absolute_path/*.php" );
1889
		}
1890
1891
		$absolute_path = untrailingslashit( $absolute_path );
1892
		$files = array();
1893
		if ( ! $dir = @opendir( $absolute_path ) ) {
1894
			return $files;
1895
		}
1896
1897
		while ( false !== $file = readdir( $dir ) ) {
1898
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
1899
				continue;
1900
			}
1901
1902
			$file = "$absolute_path/$file";
1903
1904
			if ( ! is_file( $file ) ) {
1905
				continue;
1906
			}
1907
1908
			$files[] = $file;
1909
		}
1910
1911
		closedir( $dir );
1912
1913
		return $files;
1914
	}
1915
1916
	public static function activate_new_modules( $redirect = false ) {
1917
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
1918
			return;
1919
		}
1920
1921
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
1922 View Code Duplication
		if ( ! $jetpack_old_version ) {
1923
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
1924
			/** This action is documented in class.jetpack.php */
1925
			do_action( 'updating_jetpack_version', $version, false );
1926
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1927
		}
1928
1929
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
1930
1931
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
1932
			return;
1933
		}
1934
1935
		$active_modules     = Jetpack::get_active_modules();
1936
		$reactivate_modules = array();
1937
		foreach ( $active_modules as $active_module ) {
1938
			$module = Jetpack::get_module( $active_module );
1939
			if ( ! isset( $module['changed'] ) ) {
1940
				continue;
1941
			}
1942
1943
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
1944
				continue;
1945
			}
1946
1947
			$reactivate_modules[] = $active_module;
1948
			Jetpack::deactivate_module( $active_module );
1949
		}
1950
1951
		$new_version = JETPACK__VERSION . ':' . time();
1952
		/** This action is documented in class.jetpack.php */
1953
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
1954
		Jetpack_Options::update_options(
1955
			array(
1956
				'version'     => $new_version,
1957
				'old_version' => $jetpack_old_version,
1958
			)
1959
		);
1960
1961
		Jetpack::state( 'message', 'modules_activated' );
1962
		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...
1963
1964
		if ( $redirect ) {
1965
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
1966
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
1967
				$page = $_GET['page'];
1968
			}
1969
1970
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
1971
			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...
1972
		}
1973
	}
1974
1975
	/**
1976
	 * List available Jetpack modules. Simply lists .php files in /modules/.
1977
	 * Make sure to tuck away module "library" files in a sub-directory.
1978
	 */
1979
	public static function get_available_modules( $min_version = false, $max_version = false ) {
1980
		static $modules = null;
1981
1982
		if ( ! isset( $modules ) ) {
1983
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
1984
			// Use the cache if we're on the front-end and it's available...
1985
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
1986
				$modules = $available_modules_option[ JETPACK__VERSION ];
1987
			} else {
1988
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
1989
1990
				$modules = array();
1991
1992
				foreach ( $files as $file ) {
1993
					if ( ! $headers = Jetpack::get_module( $file ) ) {
1994
						continue;
1995
					}
1996
1997
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
1998
				}
1999
2000
				Jetpack_Options::update_option( 'available_modules', array(
2001
					JETPACK__VERSION => $modules,
2002
				) );
2003
			}
2004
		}
2005
2006
		/**
2007
		 * Filters the array of modules available to be activated.
2008
		 *
2009
		 * @since 2.4.0
2010
		 *
2011
		 * @param array $modules Array of available modules.
2012
		 * @param string $min_version Minimum version number required to use modules.
2013
		 * @param string $max_version Maximum version number required to use modules.
2014
		 */
2015
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
2016
2017
		if ( ! $min_version && ! $max_version ) {
2018
			return array_keys( $mods );
2019
		}
2020
2021
		$r = array();
2022
		foreach ( $mods as $slug => $introduced ) {
2023
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2024
				continue;
2025
			}
2026
2027
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2028
				continue;
2029
			}
2030
2031
			$r[] = $slug;
2032
		}
2033
2034
		return $r;
2035
	}
2036
2037
	/**
2038
	 * Default modules loaded on activation.
2039
	 */
2040
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2041
		$return = array();
2042
2043
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
2044
			$module_data = Jetpack::get_module( $module );
2045
2046
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2047
				case 'yes' :
2048
					$return[] = $module;
2049
					break;
2050
				case 'public' :
2051
					if ( Jetpack_Options::get_option( 'public' ) ) {
2052
						$return[] = $module;
2053
					}
2054
					break;
2055
				case 'no' :
2056
				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...
2057
					break;
2058
			}
2059
		}
2060
		/**
2061
		 * Filters the array of default modules.
2062
		 *
2063
		 * @since 2.5.0
2064
		 *
2065
		 * @param array $return Array of default modules.
2066
		 * @param string $min_version Minimum version number required to use modules.
2067
		 * @param string $max_version Maximum version number required to use modules.
2068
		 */
2069
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
2070
	}
2071
2072
	/**
2073
	 * Checks activated modules during auto-activation to determine
2074
	 * if any of those modules are being deprecated.  If so, close
2075
	 * them out, and add any replacement modules.
2076
	 *
2077
	 * Runs at priority 99 by default.
2078
	 *
2079
	 * This is run late, so that it can still activate a module if
2080
	 * the new module is a replacement for another that the user
2081
	 * currently has active, even if something at the normal priority
2082
	 * would kibosh everything.
2083
	 *
2084
	 * @since 2.6
2085
	 * @uses jetpack_get_default_modules filter
2086
	 * @param array $modules
2087
	 * @return array
2088
	 */
2089
	function handle_deprecated_modules( $modules ) {
2090
		$deprecated_modules = array(
2091
			'debug'            => null,  // Closed out and moved to ./class.jetpack-debugger.php
2092
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2093
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2094
		);
2095
2096
		// Don't activate SSO if they never completed activating WPCC.
2097
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
2098
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2099
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2100
				$deprecated_modules['wpcc'] = null;
2101
			}
2102
		}
2103
2104
		foreach ( $deprecated_modules as $module => $replacement ) {
2105
			if ( Jetpack::is_module_active( $module ) ) {
2106
				self::deactivate_module( $module );
2107
				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...
2108
					$modules[] = $replacement;
2109
				}
2110
			}
2111
		}
2112
2113
		return array_unique( $modules );
2114
	}
2115
2116
	/**
2117
	 * Checks activated plugins during auto-activation to determine
2118
	 * if any of those plugins are in the list with a corresponding module
2119
	 * that is not compatible with the plugin. The module will not be allowed
2120
	 * to auto-activate.
2121
	 *
2122
	 * @since 2.6
2123
	 * @uses jetpack_get_default_modules filter
2124
	 * @param array $modules
2125
	 * @return array
2126
	 */
2127
	function filter_default_modules( $modules ) {
2128
2129
		$active_plugins = self::get_active_plugins();
2130
2131
		if ( ! empty( $active_plugins ) ) {
2132
2133
			// For each module we'd like to auto-activate...
2134
			foreach ( $modules as $key => $module ) {
2135
				// If there are potential conflicts for it...
2136
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2137
					// For each potential conflict...
2138
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2139
						// If that conflicting plugin is active...
2140
						if ( in_array( $plugin, $active_plugins ) ) {
2141
							// Remove that item from being auto-activated.
2142
							unset( $modules[ $key ] );
2143
						}
2144
					}
2145
				}
2146
			}
2147
		}
2148
2149
		return $modules;
2150
	}
2151
2152
	/**
2153
	 * Extract a module's slug from its full path.
2154
	 */
2155
	public static function get_module_slug( $file ) {
2156
		return str_replace( '.php', '', basename( $file ) );
2157
	}
2158
2159
	/**
2160
	 * Generate a module's path from its slug.
2161
	 */
2162
	public static function get_module_path( $slug ) {
2163
		return JETPACK__PLUGIN_DIR . "modules/$slug.php";
2164
	}
2165
2166
	/**
2167
	 * Load module data from module file. Headers differ from WordPress
2168
	 * plugin headers to avoid them being identified as standalone
2169
	 * plugins on the WordPress plugins page.
2170
	 */
2171
	public static function get_module( $module ) {
2172
		$headers = array(
2173
			'name'                      => 'Module Name',
2174
			'description'               => 'Module Description',
2175
			'jumpstart_desc'            => 'Jumpstart Description',
2176
			'sort'                      => 'Sort Order',
2177
			'recommendation_order'      => 'Recommendation Order',
2178
			'introduced'                => 'First Introduced',
2179
			'changed'                   => 'Major Changes In',
2180
			'deactivate'                => 'Deactivate',
2181
			'free'                      => 'Free',
2182
			'requires_connection'       => 'Requires Connection',
2183
			'auto_activate'             => 'Auto Activate',
2184
			'module_tags'               => 'Module Tags',
2185
			'feature'                   => 'Feature',
2186
			'additional_search_queries' => 'Additional Search Queries',
2187
		);
2188
2189
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2190
2191
		$mod = Jetpack::get_file_data( $file, $headers );
2192
		if ( empty( $mod['name'] ) ) {
2193
			return false;
2194
		}
2195
2196
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2197
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2198
		$mod['deactivate']              = empty( $mod['deactivate'] );
2199
		$mod['free']                    = empty( $mod['free'] );
2200
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2201
2202
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2203
			$mod['auto_activate'] = 'No';
2204
		} else {
2205
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2206
		}
2207
2208
		if ( $mod['module_tags'] ) {
2209
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2210
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2211
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2212
		} else {
2213
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2214
		}
2215
2216
		if ( $mod['feature'] ) {
2217
			$mod['feature'] = explode( ',', $mod['feature'] );
2218
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2219
		} else {
2220
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2221
		}
2222
2223
		/**
2224
		 * Filters the feature array on a module.
2225
		 *
2226
		 * This filter allows you to control where each module is filtered: Recommended,
2227
		 * Jumpstart, and the default "Other" listing.
2228
		 *
2229
		 * @since 3.5.0
2230
		 *
2231
		 * @param array   $mod['feature'] The areas to feature this module:
2232
		 *     'Jumpstart' adds to the "Jumpstart" option to activate many modules at once.
2233
		 *     'Recommended' shows on the main Jetpack admin screen.
2234
		 *     'Other' should be the default if no other value is in the array.
2235
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2236
		 * @param array   $mod All the currently assembled module data.
2237
		 */
2238
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2239
2240
		/**
2241
		 * Filter the returned data about a module.
2242
		 *
2243
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2244
		 * so please be careful.
2245
		 *
2246
		 * @since 3.6.0
2247
		 *
2248
		 * @param array   $mod    The details of the requested module.
2249
		 * @param string  $module The slug of the module, e.g. sharedaddy
2250
		 * @param string  $file   The path to the module source file.
2251
		 */
2252
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2253
	}
2254
2255
	/**
2256
	 * Like core's get_file_data implementation, but caches the result.
2257
	 */
2258
	public static function get_file_data( $file, $headers ) {
2259
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2260
		$file_name = basename( $file );
2261
		$file_data_option = Jetpack_Options::get_option( 'file_data', array() );
2262
		$key              = md5( $file_name . serialize( $headers ) );
2263
		$refresh_cache    = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2264
2265
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2266
		if ( ! $refresh_cache && isset( $file_data_option[ JETPACK__VERSION ][ $key ] ) ) {
2267
			return $file_data_option[ JETPACK__VERSION ][ $key ];
2268
		}
2269
2270
		$data = get_file_data( $file, $headers );
2271
2272
		// Strip out any old Jetpack versions that are cluttering the option.
2273
		$file_data_option = array_intersect_key( (array) $file_data_option, array( JETPACK__VERSION => null ) );
2274
		$file_data_option[ JETPACK__VERSION ][ $key ] = $data;
2275
		Jetpack_Options::update_option( 'file_data', $file_data_option );
2276
2277
		return $data;
2278
	}
2279
2280
	/**
2281
	 * Return translated module tag.
2282
	 *
2283
	 * @param string $tag Tag as it appears in each module heading.
2284
	 *
2285
	 * @return mixed
2286
	 */
2287
	public static function translate_module_tag( $tag ) {
2288
		return jetpack_get_module_i18n_tag( $tag );
2289
	}
2290
2291
	/**
2292
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2293
	 *
2294
	 * @since 3.9.2
2295
	 *
2296
	 * @param array $modules
2297
	 *
2298
	 * @return string|void
2299
	 */
2300
	public static function get_translated_modules( $modules ) {
2301
		foreach ( $modules as $index => $module ) {
2302
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2303
			if ( isset( $module['name'] ) ) {
2304
				$modules[ $index ]['name'] = $i18n_module['name'];
2305
			}
2306
			if ( isset( $module['description'] ) ) {
2307
				$modules[ $index ]['description'] = $i18n_module['description'];
2308
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2309
			}
2310
		}
2311
		return $modules;
2312
	}
2313
2314
	/**
2315
	 * Get a list of activated modules as an array of module slugs.
2316
	 */
2317
	public static function get_active_modules() {
2318
		$active = Jetpack_Options::get_option( 'active_modules' );
2319
		if ( ! is_array( $active ) )
2320
			$active = array();
2321
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2322
			$active[] = 'vaultpress';
2323
		} else {
2324
			$active = array_diff( $active, array( 'vaultpress' ) );
2325
		}
2326
2327
		//If protect is active on the main site of a multisite, it should be active on all sites.
2328
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2329
			$active[] = 'protect';
2330
		}
2331
2332
		return array_unique( $active );
2333
	}
2334
2335
	/**
2336
	 * Check whether or not a Jetpack module is active.
2337
	 *
2338
	 * @param string $module The slug of a Jetpack module.
2339
	 * @return bool
2340
	 *
2341
	 * @static
2342
	 */
2343
	public static function is_module_active( $module ) {
2344
		return in_array( $module, self::get_active_modules() );
2345
	}
2346
2347
	public static function is_module( $module ) {
2348
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2349
	}
2350
2351
	/**
2352
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2353
	 *
2354
	 * @param bool $catch True to start catching, False to stop.
2355
	 *
2356
	 * @static
2357
	 */
2358
	public static function catch_errors( $catch ) {
2359
		static $display_errors, $error_reporting;
2360
2361
		if ( $catch ) {
2362
			$display_errors  = @ini_set( 'display_errors', 1 );
2363
			$error_reporting = @error_reporting( E_ALL );
2364
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2365
		} else {
2366
			@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...
2367
			@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...
2368
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2369
		}
2370
	}
2371
2372
	/**
2373
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2374
	 */
2375
	public static function catch_errors_on_shutdown() {
2376
		Jetpack::state( 'php_errors', ob_get_clean() );
2377
	}
2378
2379
	public static function activate_default_modules( $min_version = false, $max_version = false, $other_modules = array(), $redirect = true ) {
2380
		$jetpack = Jetpack::init();
2381
2382
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2383
		$modules = array_merge( $other_modules, $modules );
2384
2385
		// Look for standalone plugins and disable if active.
2386
2387
		$to_deactivate = array();
2388
		foreach ( $modules as $module ) {
2389
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2390
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2391
			}
2392
		}
2393
2394
		$deactivated = array();
2395
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2396
			list( $probable_file, $probable_title ) = $deactivate_me;
2397
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2398
				$deactivated[] = $module;
2399
			}
2400
		}
2401
2402
		if ( $deactivated && $redirect ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2403
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2404
2405
			$url = add_query_arg(
2406
				array(
2407
					'action'   => 'activate_default_modules',
2408
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2409
				),
2410
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2411
			);
2412
			wp_safe_redirect( $url );
2413
			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...
2414
		}
2415
2416
		/**
2417
		 * Fires before default modules are activated.
2418
		 *
2419
		 * @since 1.9.0
2420
		 *
2421
		 * @param string $min_version Minimum version number required to use modules.
2422
		 * @param string $max_version Maximum version number required to use modules.
2423
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2424
		 */
2425
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2426
2427
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2428
		Jetpack::restate();
2429
		Jetpack::catch_errors( true );
2430
2431
		$active = Jetpack::get_active_modules();
2432
2433
		foreach ( $modules as $module ) {
2434
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2435
				$active[] = $module;
2436
				Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2437
				continue;
2438
			}
2439
2440
			if ( in_array( $module, $active ) ) {
2441
				$module_info = Jetpack::get_module( $module );
2442
				if ( ! $module_info['deactivate'] ) {
2443
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2444 View Code Duplication
					if ( $active_state = Jetpack::state( $state ) ) {
2445
						$active_state = explode( ',', $active_state );
2446
					} else {
2447
						$active_state = array();
2448
					}
2449
					$active_state[] = $module;
2450
					Jetpack::state( $state, implode( ',', $active_state ) );
2451
				}
2452
				continue;
2453
			}
2454
2455
			$file = Jetpack::get_module_path( $module );
2456
			if ( ! file_exists( $file ) ) {
2457
				continue;
2458
			}
2459
2460
			// we'll override this later if the plugin can be included without fatal error
2461
			if ( $redirect ) {
2462
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2463
			}
2464
			Jetpack::state( 'error', 'module_activation_failed' );
2465
			Jetpack::state( 'module', $module );
2466
			ob_start();
2467
			require $file;
2468
			/**
2469
			 * Fires when a specific module is activated.
2470
			 *
2471
			 * @since 1.9.0
2472
			 *
2473
			 * @param string $module Module slug.
2474
			 */
2475
			do_action( 'jetpack_activate_module', $module );
2476
			$active[] = $module;
2477
			$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2478 View Code Duplication
			if ( $active_state = Jetpack::state( $state ) ) {
2479
				$active_state = explode( ',', $active_state );
2480
			} else {
2481
				$active_state = array();
2482
			}
2483
			$active_state[] = $module;
2484
			Jetpack::state( $state, implode( ',', $active_state ) );
2485
			Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2486
			ob_end_clean();
2487
		}
2488
		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...
2489
		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...
2490
		Jetpack::catch_errors( false );
2491
		/**
2492
		 * Fires when default modules are activated.
2493
		 *
2494
		 * @since 1.9.0
2495
		 *
2496
		 * @param string $min_version Minimum version number required to use modules.
2497
		 * @param string $max_version Maximum version number required to use modules.
2498
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2499
		 */
2500
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2501
	}
2502
2503
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2504
		/**
2505
		 * Fires before a module is activated.
2506
		 *
2507
		 * @since 2.6.0
2508
		 *
2509
		 * @param string $module Module slug.
2510
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2511
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2512
		 */
2513
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2514
2515
		$jetpack = Jetpack::init();
2516
2517
		if ( ! strlen( $module ) )
2518
			return false;
2519
2520
		if ( ! Jetpack::is_module( $module ) )
2521
			return false;
2522
2523
		// If it's already active, then don't do it again
2524
		$active = Jetpack::get_active_modules();
2525
		foreach ( $active as $act ) {
2526
			if ( $act == $module )
2527
				return true;
2528
		}
2529
2530
		$module_data = Jetpack::get_module( $module );
2531
2532
		if ( ! Jetpack::is_active() ) {
2533
			if ( !Jetpack::is_development_mode() )
2534
				return false;
2535
2536
			// If we're not connected but in development mode, make sure the module doesn't require a connection
2537
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
2538
				return false;
2539
		}
2540
2541
		// Check and see if the old plugin is active
2542
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2543
			// Deactivate the old plugin
2544
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
2545
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
2546
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
2547
				Jetpack::state( 'deactivated_plugins', $module );
2548
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
2549
				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...
2550
			}
2551
		}
2552
2553
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
2554
		Jetpack::state( 'module', $module );
2555
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
2556
2557
		Jetpack::catch_errors( true );
2558
		ob_start();
2559
		require Jetpack::get_module_path( $module );
2560
		/** This action is documented in class.jetpack.php */
2561
		do_action( 'jetpack_activate_module', $module );
2562
		$active[] = $module;
2563
		Jetpack_Options::update_option( 'active_modules', array_unique( $active ) );
2564
		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...
2565
		Jetpack::state( 'message', 'module_activated' );
2566
		Jetpack::state( 'module', $module );
2567
		ob_end_clean();
2568
		Jetpack::catch_errors( false );
2569
2570
		// A flag for Jump Start so it's not shown again. Only set if it hasn't been yet.
2571 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2572
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2573
2574
			//Jump start is being dismissed send data to MC Stats
2575
			$jetpack->stat( 'jumpstart', 'manual,'.$module );
2576
2577
			$jetpack->do_stats( 'server_side' );
2578
		}
2579
2580
		if ( $redirect ) {
2581
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2582
		}
2583
		if ( $exit ) {
2584
			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...
2585
		}
2586
		return true;
2587
	}
2588
2589
	function activate_module_actions( $module ) {
2590
		/**
2591
		 * Fires when a module is activated.
2592
		 * The dynamic part of the filter, $module, is the module slug.
2593
		 *
2594
		 * @since 1.9.0
2595
		 *
2596
		 * @param string $module Module slug.
2597
		 */
2598
		do_action( "jetpack_activate_module_$module", $module );
2599
	}
2600
2601
	public static function deactivate_module( $module ) {
2602
		/**
2603
		 * Fires when a module is deactivated.
2604
		 *
2605
		 * @since 1.9.0
2606
		 *
2607
		 * @param string $module Module slug.
2608
		 */
2609
		do_action( 'jetpack_pre_deactivate_module', $module );
2610
2611
		$jetpack = Jetpack::init();
2612
2613
		$active = Jetpack::get_active_modules();
2614
		$new    = array_filter( array_diff( $active, (array) $module ) );
2615
2616
		/**
2617
		 * Fires when a module is deactivated.
2618
		 * The dynamic part of the filter, $module, is the module slug.
2619
		 *
2620
		 * @since 1.9.0
2621
		 *
2622
		 * @param string $module Module slug.
2623
		 */
2624
		do_action( "jetpack_deactivate_module_$module", $module );
2625
2626
		// A flag for Jump Start so it's not shown again.
2627 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
2628
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
2629
2630
			//Jump start is being dismissed send data to MC Stats
2631
			$jetpack->stat( 'jumpstart', 'manual,deactivated-'.$module );
2632
2633
			$jetpack->do_stats( 'server_side' );
2634
		}
2635
2636
		$success = Jetpack_Options::update_option( 'active_modules', array_unique( $new ) );
2637
		/**
2638
		 * Fired after a module has been deactivated.
2639
		 *
2640
		 * @since 4.1
2641
		 *
2642
		 * @param string $module Module slug.
2643
		 * @param boolean $success whether the module was deactivated.
2644
		 */
2645
		do_action( 'jetpack_deactivate_module', $module, $success );
2646
2647
		return $success;
2648
	}
2649
2650
	public static function enable_module_configurable( $module ) {
2651
		$module = Jetpack::get_module_slug( $module );
2652
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
2653
	}
2654
2655
	public static function module_configuration_url( $module ) {
2656
		$module = Jetpack::get_module_slug( $module );
2657
		return Jetpack::admin_url( array( 'page' => 'jetpack', 'configure' => $module ) );
2658
	}
2659
2660
	public static function module_configuration_load( $module, $method ) {
2661
		$module = Jetpack::get_module_slug( $module );
2662
		add_action( 'jetpack_module_configuration_load_' . $module, $method );
2663
	}
2664
2665
	public static function module_configuration_head( $module, $method ) {
2666
		$module = Jetpack::get_module_slug( $module );
2667
		add_action( 'jetpack_module_configuration_head_' . $module, $method );
2668
	}
2669
2670
	public static function module_configuration_screen( $module, $method ) {
2671
		$module = Jetpack::get_module_slug( $module );
2672
		add_action( 'jetpack_module_configuration_screen_' . $module, $method );
2673
	}
2674
2675
	public static function module_configuration_activation_screen( $module, $method ) {
2676
		$module = Jetpack::get_module_slug( $module );
2677
		add_action( 'display_activate_module_setting_' . $module, $method );
2678
	}
2679
2680
/* Installation */
2681
2682
	public static function bail_on_activation( $message, $deactivate = true ) {
2683
?>
2684
<!doctype html>
2685
<html>
2686
<head>
2687
<meta charset="<?php bloginfo( 'charset' ); ?>">
2688
<style>
2689
* {
2690
	text-align: center;
2691
	margin: 0;
2692
	padding: 0;
2693
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
2694
}
2695
p {
2696
	margin-top: 1em;
2697
	font-size: 18px;
2698
}
2699
</style>
2700
<body>
2701
<p><?php echo esc_html( $message ); ?></p>
2702
</body>
2703
</html>
2704
<?php
2705
		if ( $deactivate ) {
2706
			$plugins = get_option( 'active_plugins' );
2707
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
2708
			$update  = false;
2709
			foreach ( $plugins as $i => $plugin ) {
2710
				if ( $plugin === $jetpack ) {
2711
					$plugins[$i] = false;
2712
					$update = true;
2713
				}
2714
			}
2715
2716
			if ( $update ) {
2717
				update_option( 'active_plugins', array_filter( $plugins ) );
2718
			}
2719
		}
2720
		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...
2721
	}
2722
2723
	/**
2724
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
2725
	 * @static
2726
	 */
2727
	public static function plugin_activation( $network_wide ) {
2728
		Jetpack_Options::update_option( 'activated', 1 );
2729
2730
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
2731
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
2732
		}
2733
2734
		if ( $network_wide )
2735
			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...
2736
2737
		Jetpack::plugin_initialize();
2738
	}
2739
	/**
2740
	 * Runs before bumping version numbers up to a new version
2741
	 * @param  (string) $version    Version:timestamp
2742
	 * @param  (string) $old_version Old Version:timestamp or false if not set yet.
2743
	 * @return null              [description]
2744
	 */
2745
	public static function do_version_bump( $version, $old_version ) {
2746
2747
		if ( ! $old_version ) { // For new sites
2748
			// Setting up jetpack manage
2749
			Jetpack::activate_manage();
2750
		}
2751
	}
2752
2753
	/**
2754
	 * Sets the internal version number and activation state.
2755
	 * @static
2756
	 */
2757
	public static function plugin_initialize() {
2758
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
2759
			Jetpack_Options::update_option( 'activated', 2 );
2760
		}
2761
2762 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
2763
			$version = $old_version = JETPACK__VERSION . ':' . time();
2764
			/** This action is documented in class.jetpack.php */
2765
			do_action( 'updating_jetpack_version', $version, false );
2766
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2767
		}
2768
2769
		Jetpack::load_modules();
2770
2771
		Jetpack_Options::delete_option( 'do_activate' );
2772
	}
2773
2774
	/**
2775
	 * Removes all connection options
2776
	 * @static
2777
	 */
2778
	public static function plugin_deactivation( ) {
2779
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
2780
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
2781
			Jetpack_Network::init()->deactivate();
2782
		} else {
2783
			Jetpack::disconnect( false );
2784
			//Jetpack_Heartbeat::init()->deactivate();
2785
		}
2786
	}
2787
2788
	/**
2789
	 * Disconnects from the Jetpack servers.
2790
	 * Forgets all connection details and tells the Jetpack servers to do the same.
2791
	 * @static
2792
	 */
2793
	public static function disconnect( $update_activated_state = true ) {
2794
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
2795
		Jetpack::clean_nonces( true );
2796
2797
		Jetpack::load_xml_rpc_client();
2798
		$xml = new Jetpack_IXR_Client();
2799
		$xml->query( 'jetpack.deregister' );
2800
2801
		Jetpack_Options::delete_option(
2802
			array(
2803
				'register',
2804
				'blog_token',
2805
				'user_token',
2806
				'user_tokens',
2807
				'master_user',
2808
				'time_diff',
2809
				'fallback_no_verify_ssl_certs',
2810
			)
2811
		);
2812
2813
		if ( $update_activated_state ) {
2814
			Jetpack_Options::update_option( 'activated', 4 );
2815
		}
2816
2817
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
2818
		// Check then record unique disconnection if site has never been disconnected previously
2819
		if ( -1 == $jetpack_unique_connection['disconnected'] ) {
2820
			$jetpack_unique_connection['disconnected'] = 1;
2821
		}
2822
		else {
2823
			if ( 0 == $jetpack_unique_connection['disconnected'] ) {
2824
				//track unique disconnect
2825
				$jetpack = Jetpack::init();
2826
2827
				$jetpack->stat( 'connections', 'unique-disconnect' );
2828
				$jetpack->do_stats( 'server_side' );
2829
			}
2830
			// increment number of times disconnected
2831
			$jetpack_unique_connection['disconnected'] += 1;
2832
		}
2833
2834
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
2835
2836
		// Disable the Heartbeat cron
2837
		Jetpack_Heartbeat::init()->deactivate();
2838
	}
2839
2840
	/**
2841
	 * Unlinks the current user from the linked WordPress.com user
2842
	 */
2843
	public static function unlink_user( $user_id = null ) {
2844
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
2845
			return false;
2846
2847
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
2848
2849
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
2850
			return false;
2851
2852
		if ( ! isset( $tokens[ $user_id ] ) )
2853
			return false;
2854
2855
		Jetpack::load_xml_rpc_client();
2856
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
2857
		$xml->query( 'jetpack.unlink_user', $user_id );
2858
2859
		unset( $tokens[ $user_id ] );
2860
2861
		Jetpack_Options::update_option( 'user_tokens', $tokens );
2862
2863
		/**
2864
		 * Fires after the current user has been unlinked from WordPress.com.
2865
		 *
2866
		 * @since 4.1.0
2867
		 *
2868
		 * @param int $user_id The current user's ID.
2869
		 */
2870
		do_action( 'jetpack_unlinked_user', $user_id );
2871
2872
		return true;
2873
	}
2874
2875
	/**
2876
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
2877
	 */
2878
	public static function try_registration() {
2879
		// Let's get some testing in beta versions and such.
2880
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
2881
			// Before attempting to connect, let's make sure that the domains are viable.
2882
			$domains_to_check = array_unique( array(
2883
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
2884
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
2885
			) );
2886
			foreach ( $domains_to_check as $domain ) {
2887
				$result = Jetpack_Data::is_usable_domain( $domain );
2888
				if ( is_wp_error( $result ) ) {
2889
					return $result;
2890
				}
2891
			}
2892
		}
2893
2894
		$result = Jetpack::register();
2895
2896
		// If there was an error with registration and the site was not registered, record this so we can show a message.
2897
		if ( ! $result || is_wp_error( $result ) ) {
2898
			return $result;
2899
		} else {
2900
			return true;
2901
		}
2902
	}
2903
2904
	/**
2905
	 * Tracking an internal event log. Try not to put too much chaff in here.
2906
	 *
2907
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
2908
	 */
2909
	public static function log( $code, $data = null ) {
2910
		// only grab the latest 200 entries
2911
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
2912
2913
		// Append our event to the log
2914
		$log_entry = array(
2915
			'time'    => time(),
2916
			'user_id' => get_current_user_id(),
2917
			'blog_id' => Jetpack_Options::get_option( 'id' ),
2918
			'code'    => $code,
2919
		);
2920
		// Don't bother storing it unless we've got some.
2921
		if ( ! is_null( $data ) ) {
2922
			$log_entry['data'] = $data;
2923
		}
2924
		$log[] = $log_entry;
2925
2926
		// Try add_option first, to make sure it's not autoloaded.
2927
		// @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...
2928
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
2929
			Jetpack_Options::update_option( 'log', $log );
2930
		}
2931
2932
		/**
2933
		 * Fires when Jetpack logs an internal event.
2934
		 *
2935
		 * @since 3.0.0
2936
		 *
2937
		 * @param array $log_entry {
2938
		 *	Array of details about the log entry.
2939
		 *
2940
		 *	@param string time Time of the event.
2941
		 *	@param int user_id ID of the user who trigerred the event.
2942
		 *	@param int blog_id Jetpack Blog ID.
2943
		 *	@param string code Unique name for the event.
2944
		 *	@param string data Data about the event.
2945
		 * }
2946
		 */
2947
		do_action( 'jetpack_log_entry', $log_entry );
2948
	}
2949
2950
	/**
2951
	 * Get the internal event log.
2952
	 *
2953
	 * @param $event (string) - only return the specific log events
2954
	 * @param $num   (int)    - get specific number of latest results, limited to 200
2955
	 *
2956
	 * @return array of log events || WP_Error for invalid params
2957
	 */
2958
	public static function get_log( $event = false, $num = false ) {
2959
		if ( $event && ! is_string( $event ) ) {
2960
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
2961
		}
2962
2963
		if ( $num && ! is_numeric( $num ) ) {
2964
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
2965
		}
2966
2967
		$entire_log = Jetpack_Options::get_option( 'log', array() );
2968
2969
		// If nothing set - act as it did before, otherwise let's start customizing the output
2970
		if ( ! $num && ! $event ) {
2971
			return $entire_log;
2972
		} else {
2973
			$entire_log = array_reverse( $entire_log );
2974
		}
2975
2976
		$custom_log_output = array();
2977
2978
		if ( $event ) {
2979
			foreach ( $entire_log as $log_event ) {
2980
				if ( $event == $log_event[ 'code' ] ) {
2981
					$custom_log_output[] = $log_event;
2982
				}
2983
			}
2984
		} else {
2985
			$custom_log_output = $entire_log;
2986
		}
2987
2988
		if ( $num ) {
2989
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
2990
		}
2991
2992
		return $custom_log_output;
2993
	}
2994
2995
	/**
2996
	 * Log modification of important settings.
2997
	 */
2998
	public static function log_settings_change( $option, $old_value, $value ) {
0 ignored issues
show
Unused Code introduced by
The parameter $old_value 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...
2999
		switch( $option ) {
3000
			case 'jetpack_sync_non_public_post_stati':
3001
				self::log( $option, $value );
3002
				break;
3003
		}
3004
	}
3005
3006
	/**
3007
	 * Return stat data for WPCOM sync
3008
	 */
3009
	function get_stat_data() {
3010
		$heartbeat_data = Jetpack_Heartbeat::generate_stats_array();
3011
		$additional_data = $this->get_additional_stat_data();
3012
3013
		return json_encode( array_merge( $heartbeat_data, $additional_data ) );
3014
	}
3015
3016
	/**
3017
	 * Get additional stat data to sync to WPCOM
3018
	 */
3019
	function get_additional_stat_data( $prefix = '' ) {
3020
		$return["{$prefix}themes"]         = Jetpack::get_parsed_theme_data();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
3021
		$return["{$prefix}plugins-extra"]  = Jetpack::get_parsed_plugin_data();
3022
		$return["{$prefix}users"]          = count_users();
3023
		$return["{$prefix}site-count"]     = 0;
3024
		if ( function_exists( 'get_blog_count' ) ) {
3025
			$return["{$prefix}site-count"] = get_blog_count();
3026
		}
3027
		return $return;
3028
	}
3029
3030
	/* Admin Pages */
3031
3032
	function admin_init() {
3033
		// If the plugin is not connected, display a connect message.
3034
		if (
3035
			// the plugin was auto-activated and needs its candy
3036
			Jetpack_Options::get_option( 'do_activate' )
3037
		||
3038
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3039
			! Jetpack_Options::get_option( 'activated' )
3040
		) {
3041
			Jetpack::plugin_initialize();
3042
		}
3043
3044
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
3045
			if ( 4 != Jetpack_Options::get_option( 'activated' ) ) {
3046
				// Show connect notice on dashboard and plugins pages
3047
				add_action( 'load-index.php', array( $this, 'prepare_connect_notice' ) );
3048
				add_action( 'load-plugins.php', array( $this, 'prepare_connect_notice' ) );
3049
			}
3050
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3051
			// Upgrade: 1.1 -> 1.1.1
3052
			// Check and see if host can verify the Jetpack servers' SSL certificate
3053
			$args = array();
3054
			Jetpack_Client::_wp_remote_request(
3055
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
3056
				$args,
3057
				true
3058
			);
3059
		} else {
3060
			// Show the notice on the Dashboard only for now
3061
3062
			add_action( 'load-index.php', array( $this, 'prepare_manage_jetpack_notice' ) );
3063
3064
			// Identity crisis notices
3065
			add_action( 'jetpack_notices', array( $this, 'alert_identity_crisis' ) );
3066
		}
3067
3068
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3069
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3070
		}
3071
3072
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3073
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3074
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3075
3076
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
3077
			// Artificially throw errors in certain whitelisted cases during plugin activation
3078
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3079
3080
			// Kick off synchronization of user role when it changes
3081
			add_action( 'set_user_role', array( $this, 'user_role_change' ) );
3082
		}
3083
3084
		// Jetpack Manage Activation Screen from .com
3085
		Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
3086
	}
3087
3088
	function admin_body_class( $admin_body_class = '' ) {
3089
		$classes = explode( ' ', trim( $admin_body_class ) );
3090
3091
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3092
3093
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3094
		return " $admin_body_class ";
3095
	}
3096
3097
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3098
		return $admin_body_class . ' jetpack-pagestyles ';
3099
	}
3100
3101
	function prepare_connect_notice() {
3102
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3103
3104
		add_action( 'admin_notices', array( $this, 'admin_connect_notice' ) );
3105
3106
		if ( Jetpack::state( 'network_nag' ) )
3107
			add_action( 'network_admin_notices', array( $this, 'network_connect_notice' ) );
3108
	}
3109
	/**
3110
	 * Call this function if you want the Big Jetpack Manage Notice to show up.
3111
	 *
3112
	 * @return null
3113
	 */
3114
	function prepare_manage_jetpack_notice() {
3115
3116
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3117
		add_action( 'admin_notices', array( $this, 'admin_jetpack_manage_notice' ) );
3118
	}
3119
3120
	function manage_activate_screen() {
3121
		include ( JETPACK__PLUGIN_DIR . 'modules/manage/activate-admin.php' );
3122
	}
3123
	/**
3124
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3125
	 * This function artificially throws errors for such cases (whitelisted).
3126
	 *
3127
	 * @param string $plugin The activated plugin.
3128
	 */
3129
	function throw_error_on_activate_plugin( $plugin ) {
3130
		$active_modules = Jetpack::get_active_modules();
3131
3132
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3133
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3134
			$throw = false;
3135
3136
			// Try and make sure it really was the stats plugin
3137
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3138
				if ( 'stats.php' == basename( $plugin ) ) {
3139
					$throw = true;
3140
				}
3141
			} else {
3142
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3143
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3144
					$throw = true;
3145
				}
3146
			}
3147
3148
			if ( $throw ) {
3149
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3150
			}
3151
		}
3152
	}
3153
3154
	function intercept_plugin_error_scrape_init() {
3155
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3156
	}
3157
3158
	function intercept_plugin_error_scrape( $action, $result ) {
3159
		if ( ! $result ) {
3160
			return;
3161
		}
3162
3163
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3164
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3165
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3166
			}
3167
		}
3168
	}
3169
3170
	function add_remote_request_handlers() {
3171
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
3172
	}
3173
3174
	function remote_request_handlers() {
3175
		switch ( current_filter() ) {
3176
		case 'wp_ajax_nopriv_jetpack_upload_file' :
3177
			$response = $this->upload_handler();
3178
			break;
3179
		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...
3180
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
3181
			break;
3182
		}
3183
3184
		if ( ! $response ) {
3185
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
3186
		}
3187
3188
		if ( is_wp_error( $response ) ) {
3189
			$status_code       = $response->get_error_data();
3190
			$error             = $response->get_error_code();
3191
			$error_description = $response->get_error_message();
3192
3193
			if ( ! is_int( $status_code ) ) {
3194
				$status_code = 400;
3195
			}
3196
3197
			status_header( $status_code );
3198
			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...
3199
		}
3200
3201
		status_header( 200 );
3202
		if ( true === $response ) {
3203
			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...
3204
		}
3205
3206
		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...
3207
	}
3208
3209
	function upload_handler() {
3210
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3211
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
3212
		}
3213
3214
		$user = wp_authenticate( '', '' );
3215
		if ( ! $user || is_wp_error( $user ) ) {
3216
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
3217
		}
3218
3219
		wp_set_current_user( $user->ID );
3220
3221
		if ( ! current_user_can( 'upload_files' ) ) {
3222
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3223
		}
3224
3225
		if ( empty( $_FILES ) ) {
3226
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3227
		}
3228
3229
		foreach ( array_keys( $_FILES ) as $files_key ) {
3230
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3231
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3232
			}
3233
		}
3234
3235
		$media_keys = array_keys( $_FILES['media'] );
3236
3237
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
3238
		if ( ! $token || is_wp_error( $token ) ) {
3239
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3240
		}
3241
3242
		$uploaded_files = array();
3243
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3244
		unset( $GLOBALS['post'] );
3245
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3246
			$file = array();
3247
			foreach ( $media_keys as $media_key ) {
3248
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3249
			}
3250
3251
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3252
3253
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3254
			if ( $hmac_provided !== $hmac_file ) {
3255
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3256
				continue;
3257
			}
3258
3259
			$_FILES['.jetpack.upload.'] = $file;
3260
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3261
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3262
				$post_id = 0;
3263
			}
3264
			$attachment_id = media_handle_upload(
3265
				'.jetpack.upload.',
3266
				$post_id,
3267
				array(),
3268
				array(
3269
					'action' => 'jetpack_upload_file',
3270
				)
3271
			);
3272
3273
			if ( ! $attachment_id ) {
3274
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3275
			} elseif ( is_wp_error( $attachment_id ) ) {
3276
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3277
			} else {
3278
				$attachment = get_post( $attachment_id );
3279
				$uploaded_files[$index] = (object) array(
3280
					'id'   => (string) $attachment_id,
3281
					'file' => $attachment->post_title,
3282
					'url'  => wp_get_attachment_url( $attachment_id ),
3283
					'type' => $attachment->post_mime_type,
3284
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3285
				);
3286
			}
3287
		}
3288
		if ( ! is_null( $global_post ) ) {
3289
			$GLOBALS['post'] = $global_post;
3290
		}
3291
3292
		return $uploaded_files;
3293
	}
3294
3295
	/**
3296
	 * Add help to the Jetpack page
3297
	 *
3298
	 * @since Jetpack (1.2.3)
3299
	 * @return false if not the Jetpack page
3300
	 */
3301
	function admin_help() {
3302
		$current_screen = get_current_screen();
3303
3304
		// Overview
3305
		$current_screen->add_help_tab(
3306
			array(
3307
				'id'		=> 'home',
3308
				'title'		=> __( 'Home', 'jetpack' ),
3309
				'content'	=>
3310
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3311
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3312
					'<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>',
3313
			)
3314
		);
3315
3316
		// Screen Content
3317
		if ( current_user_can( 'manage_options' ) ) {
3318
			$current_screen->add_help_tab(
3319
				array(
3320
					'id'		=> 'settings',
3321
					'title'		=> __( 'Settings', 'jetpack' ),
3322
					'content'	=>
3323
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3324
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3325
						'<ol>' .
3326
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3327
							'<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>' .
3328
						'</ol>' .
3329
						'<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>'
3330
				)
3331
			);
3332
		}
3333
3334
		// Help Sidebar
3335
		$current_screen->set_help_sidebar(
3336
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3337
			'<p><a href="https://jetpack.com/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
3338
			'<p><a href="https://jetpack.com/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3339
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3340
		);
3341
	}
3342
3343
	function admin_menu_css() {
3344
		wp_enqueue_style( 'jetpack-icons' );
3345
	}
3346
3347
	function admin_menu_order() {
3348
		return true;
3349
	}
3350
3351 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3352
		$jp_menu_order = array();
3353
3354
		foreach ( $menu_order as $index => $item ) {
3355
			if ( $item != 'jetpack' ) {
3356
				$jp_menu_order[] = $item;
3357
			}
3358
3359
			if ( $index == 0 ) {
3360
				$jp_menu_order[] = 'jetpack';
3361
			}
3362
		}
3363
3364
		return $jp_menu_order;
3365
	}
3366
3367
	function admin_head() {
3368 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) )
3369
			/** This action is documented in class.jetpack-admin-page.php */
3370
			do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
3371
	}
3372
3373
	function admin_banner_styles() {
3374
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3375
3376
		wp_enqueue_style( 'jetpack', plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION . '-20121016' );
3377
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3378
		wp_style_add_data( 'jetpack', 'suffix', $min );
3379
	}
3380
3381
	function admin_scripts() {
3382
		wp_enqueue_script( 'jetpack-js', plugins_url( '_inc/jp.js', JETPACK__PLUGIN_FILE ), array( 'jquery', 'wp-util' ), JETPACK__VERSION . '-20121111' );
3383
		wp_localize_script(
3384
			'jetpack-js',
3385
			'jetpackL10n',
3386
			array(
3387
				'ays_disconnect' => "This will deactivate all Jetpack modules.\nAre you sure you want to disconnect?",
3388
				'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?",
3389
				'ays_dismiss'    => "This will deactivate Jetpack.\nAre you sure you want to deactivate Jetpack?",
3390
			)
3391
		);
3392
		add_action( 'admin_footer', array( $this, 'do_stats' ) );
3393
	}
3394
3395
	function plugin_action_links( $actions ) {
3396
3397
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), __( 'Jetpack', 'jetpack' ) ) );
3398
3399
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
3400
			return array_merge(
3401
				$jetpack_home,
3402
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack_modules' ), __( 'Settings', 'jetpack' ) ) ),
3403
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
3404
				$actions
3405
				);
3406
			}
3407
3408
		return array_merge( $jetpack_home, $actions );
3409
	}
3410
3411
	function admin_connect_notice() {
3412
		// Don't show the connect notice anywhere but the plugins.php after activating
3413
		$current = get_current_screen();
3414
		if ( 'plugins' !== $current->parent_base )
3415
			return;
3416
3417
		if ( ! current_user_can( 'jetpack_connect' ) )
3418
			return;
3419
3420
		$dismiss_and_deactivate_url = wp_nonce_url( Jetpack::admin_url( '?page=jetpack&jetpack-notice=dismiss' ), 'jetpack-deactivate' );
3421
		?>
3422
		<div id="message" class="updated jp-banner">
3423
			<a href="<?php echo esc_url( $dismiss_and_deactivate_url ); ?>" class="notice-dismiss" title="<?php esc_attr_e( 'Dismiss this notice', 'jetpack' ); ?>"></a>
3424
			<?php if ( in_array( Jetpack_Options::get_option( 'activated' ) , array( 1, 2, 3 ) ) ) : ?>
3425
					<div class="jp-banner__description-container">
3426
						<h2 class="jp-banner__header"><?php _e( 'Your Jetpack is almost ready!', 'jetpack' ); ?></h2>
3427
						<p class="jp-banner__description"><?php _e( 'Please connect to or create a WordPress.com account to enable Jetpack, including powerful security, traffic, and customization services.', 'jetpack' ); ?></p>
3428
						<p class="jp-banner__button-container">
3429
							<a href="<?php echo $this->build_connect_url( false, false, 'banner' ) ?>" class="button button-primary" id="wpcom-connect"><?php _e( 'Connect to WordPress.com', 'jetpack' ); ?></a>
3430
							<a href="<?php echo Jetpack::admin_url( 'admin.php?page=jetpack' ) ?>" class="button" title="<?php esc_attr_e( 'Learn about the benefits you receive when you connect Jetpack to WordPress.com', 'jetpack' ); ?>"><?php _e( 'Learn more', 'jetpack' ); ?></a>
3431
						</p>
3432
					</div>
3433 View Code Duplication
			<?php else : ?>
3434
				<div class="jp-banner__content">
3435
					<h2><?php _e( 'Jetpack is installed!', 'jetpack' ) ?></h2>
3436
					<p><?php _e( 'It\'s ready to bring awesome, WordPress.com cloud-powered features to your site.', 'jetpack' ) ?></p>
3437
				</div>
3438
				<div class="jp-banner__action-container">
3439
					<a href="<?php echo Jetpack::admin_url() ?>" class="jp-banner__button" id="wpcom-connect"><?php _e( 'Learn More', 'jetpack' ); ?></a>
3440
				</div>
3441
			<?php endif; ?>
3442
		</div>
3443
3444
		<?php
3445
	}
3446
3447
	/**
3448
	 * This is the first banner
3449
	 * It should be visible only to user that can update the option
3450
	 * Are not connected
3451
	 *
3452
	 * @return null
3453
	 */
3454
	function admin_jetpack_manage_notice() {
3455
		$screen = get_current_screen();
3456
3457
		// Don't show the connect notice on the jetpack settings page.
3458
		if ( ! in_array( $screen->base, array( 'dashboard' ) ) || $screen->is_network || $screen->action )
3459
			return;
3460
3461
		// Only show it if don't have the managment option set.
3462
		// And not dismissed it already.
3463
		if ( ! $this->can_display_jetpack_manage_notice() || Jetpack_Options::get_option( 'dismissed_manage_banner' ) ) {
3464
			return;
3465
		}
3466
3467
		$opt_out_url = $this->opt_out_jetpack_manage_url();
3468
		$opt_in_url  = $this->opt_in_jetpack_manage_url();
3469
		/**
3470
		 * I think it would be great to have different wordsing depending on where you are
3471
		 * for example if we show the notice on dashboard and a different one if we show it on Plugins screen
3472
		 * etc..
3473
		 */
3474
3475
		?>
3476
		<div id="message" class="updated jp-banner">
3477
				<a href="<?php echo esc_url( $opt_out_url ); ?>" class="notice-dismiss" title="<?php esc_attr_e( 'Dismiss this notice', 'jetpack' ); ?>"></a>
3478
				<div class="jp-banner__description-container">
3479
					<h2 class="jp-banner__header"><?php esc_html_e( 'Jetpack Centralized Site Management', 'jetpack' ); ?></h2>
3480
					<p class="jp-banner__description"><?php printf( __( 'Manage multiple Jetpack enabled sites from one single dashboard at wordpress.com. Allows all existing, connected Administrators to modify your site.', 'jetpack' ), 'https://jetpack.com/support/site-management' ); ?></p>
3481
					<p class="jp-banner__button-container">
3482
						<a href="<?php echo esc_url( $opt_in_url ); ?>" class="button button-primary" id="wpcom-connect"><?php _e( 'Activate Jetpack Manage', 'jetpack' ); ?></a>
3483
						<a href="https://jetpack.com/support/site-management" class="button" target="_blank" title="<?php esc_attr_e( 'Learn more about Jetpack Manage on Jetpack.com', 'jetpack' ); ?>"><?php _e( 'Learn more', 'jetpack' ); ?></a>
3484
					</p>
3485
				</div>
3486
		</div>
3487
		<?php
3488
	}
3489
3490
	/**
3491
	 * Returns the url that the user clicks to remove the notice for the big banner
3492
	 * @return (string)
3493
	 */
3494
	function opt_out_jetpack_manage_url() {
3495
		$referer = '&_wp_http_referer=' . add_query_arg( '_wp_http_referer', null );
3496
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-out' . $referer ), 'jetpack_manage_banner_opt_out' );
3497
	}
3498
	/**
3499
	 * Returns the url that the user clicks to opt in to Jetpack Manage
3500
	 * @return (string)
3501
	 */
3502
	function opt_in_jetpack_manage_url() {
3503
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-in' ), 'jetpack_manage_banner_opt_in' );
3504
	}
3505
3506
	function opt_in_jetpack_manage_notice() {
3507
		?>
3508
		<div class="wrap">
3509
			<div id="message" class="jetpack-message is-opt-in">
3510
				<?php echo sprintf( __( '<p><a href="%1$s" title="Opt in to WordPress.com Site Management" >Activate Site Management</a> to manage multiple sites from our centralized dashboard at wordpress.com/sites. <a href="%2$s" target="_blank">Learn more</a>.</p><a href="%1$s" class="jp-button">Activate Now</a>', 'jetpack' ), $this->opt_in_jetpack_manage_url(), 'https://jetpack.com/support/site-management' ); ?>
3511
			</div>
3512
		</div>
3513
		<?php
3514
3515
	}
3516
	/**
3517
	 * Determines whether to show the notice of not true = display notice
3518
	 * @return (bool)
3519
	 */
3520
	function can_display_jetpack_manage_notice() {
3521
		// never display the notice to users that can't do anything about it anyways
3522
		if( ! current_user_can( 'jetpack_manage_modules' ) )
3523
			return false;
3524
3525
		// don't display if we are in development more
3526
		if( Jetpack::is_development_mode() ) {
3527
			return false;
3528
		}
3529
		// don't display if the site is private
3530
		if(  ! Jetpack_Options::get_option( 'public' ) )
3531
			return false;
3532
3533
		/**
3534
		 * Should the Jetpack Remote Site Management notice be displayed.
3535
		 *
3536
		 * @since 3.3.0
3537
		 *
3538
		 * @param bool ! self::is_module_active( 'manage' ) Is the Manage module inactive.
3539
		 */
3540
		return apply_filters( 'can_display_jetpack_manage_notice', ! self::is_module_active( 'manage' ) );
3541
	}
3542
3543
	function network_connect_notice() {
3544
		?>
3545
		<div id="message" class="updated jetpack-message">
3546
			<div class="squeezer">
3547
				<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>
3548
			</div>
3549
		</div>
3550
		<?php
3551
	}
3552
3553
	/*
3554
	 * Registration flow:
3555
	 * 1 - ::admin_page_load() action=register
3556
	 * 2 - ::try_registration()
3557
	 * 3 - ::register()
3558
	 *     - Creates jetpack_register option containing two secrets and a timestamp
3559
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
3560
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
3561
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
3562
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
3563
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
3564
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
3565
	 *       jetpack_id, jetpack_secret, jetpack_public
3566
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
3567
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
3568
	 * 5 - user logs in with WP.com account
3569
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
3570
	 *		- Jetpack_Client_Server::authorize()
3571
	 *		- Jetpack_Client_Server::get_token()
3572
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
3573
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
3574
	 *			- which responds with access_token, token_type, scope
3575
	 *		- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
3576
	 *		- Jetpack::activate_default_modules()
3577
	 *     		- Deactivates deprecated plugins
3578
	 *     		- Activates all default modules
3579
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
3580
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
3581
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
3582
	 *     Done!
3583
	 */
3584
3585
	/**
3586
	 * Handles the page load events for the Jetpack admin page
3587
	 */
3588
	function admin_page_load() {
3589
		$error = false;
3590
3591
		// Make sure we have the right body class to hook stylings for subpages off of.
3592
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
3593
3594
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
3595
			// Should only be used in intermediate redirects to preserve state across redirects
3596
			Jetpack::restate();
3597
		}
3598
3599
		if ( isset( $_GET['connect_url_redirect'] ) ) {
3600
			// User clicked in the iframe to link their accounts
3601
			if ( ! Jetpack::is_user_connected() ) {
3602
				$connect_url = $this->build_connect_url( true, false, 'iframe' );
3603
				if ( isset( $_GET['notes_iframe'] ) )
3604
					$connect_url .= '&notes_iframe';
3605
				wp_redirect( $connect_url );
3606
				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...
3607
			} else {
3608
				Jetpack::state( 'message', 'already_authorized' );
3609
				wp_safe_redirect( Jetpack::admin_url() );
3610
				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...
3611
			}
3612
		}
3613
3614
3615
		if ( isset( $_GET['action'] ) ) {
3616
			switch ( $_GET['action'] ) {
3617
			case 'authorize':
3618
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
3619
					Jetpack::state( 'message', 'already_authorized' );
3620
					wp_safe_redirect( Jetpack::admin_url() );
3621
					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...
3622
				}
3623
				Jetpack::log( 'authorize' );
3624
				$client_server = new Jetpack_Client_Server;
3625
				$client_server->client_authorize();
3626
				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...
3627
			case 'register' :
3628
				if ( ! current_user_can( 'jetpack_connect' ) ) {
3629
					$error = 'cheatin';
3630
					break;
3631
				}
3632
				check_admin_referer( 'jetpack-register' );
3633
				Jetpack::log( 'register' );
3634
				Jetpack::maybe_set_version_option();
3635
				$registered = Jetpack::try_registration();
3636
				if ( is_wp_error( $registered ) ) {
3637
					$error = $registered->get_error_code();
3638
					Jetpack::state( 'error_description', $error );
3639
					Jetpack::state( 'error_description', $registered->get_error_message() );
3640
					break;
3641
				}
3642
3643
				$from = isset( $_GET['from'] ) ? $_GET['from'] : false;
3644
3645
				wp_redirect( $this->build_connect_url( true, false, $from ) );
3646
				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...
3647
			case 'activate' :
3648
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
3649
					$error = 'cheatin';
3650
					break;
3651
				}
3652
3653
				$module = stripslashes( $_GET['module'] );
3654
				check_admin_referer( "jetpack_activate-$module" );
3655
				Jetpack::log( 'activate', $module );
3656
				Jetpack::activate_module( $module );
3657
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
3658
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3659
				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...
3660
			case 'activate_default_modules' :
3661
				check_admin_referer( 'activate_default_modules' );
3662
				Jetpack::log( 'activate_default_modules' );
3663
				Jetpack::restate();
3664
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
3665
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
3666
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
3667
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
3668
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3669
				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...
3670
			case 'disconnect' :
3671
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
3672
					$error = 'cheatin';
3673
					break;
3674
				}
3675
3676
				check_admin_referer( 'jetpack-disconnect' );
3677
				Jetpack::log( 'disconnect' );
3678
				Jetpack::disconnect();
3679
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
3680
				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...
3681
			case 'reconnect' :
3682
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
3683
					$error = 'cheatin';
3684
					break;
3685
				}
3686
3687
				check_admin_referer( 'jetpack-reconnect' );
3688
				Jetpack::log( 'reconnect' );
3689
				$this->disconnect();
3690
				wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
3691
				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...
3692 View Code Duplication
			case 'deactivate' :
3693
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
3694
					$error = 'cheatin';
3695
					break;
3696
				}
3697
3698
				$modules = stripslashes( $_GET['module'] );
3699
				check_admin_referer( "jetpack_deactivate-$modules" );
3700
				foreach ( explode( ',', $modules ) as $module ) {
3701
					Jetpack::log( 'deactivate', $module );
3702
					Jetpack::deactivate_module( $module );
3703
					Jetpack::state( 'message', 'module_deactivated' );
3704
				}
3705
				Jetpack::state( 'module', $modules );
3706
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3707
				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...
3708
			case 'unlink' :
3709
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
3710
				check_admin_referer( 'jetpack-unlink' );
3711
				Jetpack::log( 'unlink' );
3712
				$this->unlink_user();
3713
				Jetpack::state( 'message', 'unlinked' );
3714
				if ( 'sub-unlink' == $redirect ) {
3715
					wp_safe_redirect( admin_url() );
3716
				} else {
3717
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
3718
				}
3719
				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...
3720
			default:
3721
				/**
3722
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
3723
				 *
3724
				 * @since 2.6.0
3725
				 *
3726
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
3727
				 */
3728
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
3729
			}
3730
		}
3731
3732
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
3733
			self::activate_new_modules( true );
3734
		}
3735
3736
		$message_code = Jetpack::state( 'message' );
3737
		if ( Jetpack::state( 'optin-manage' ) ) {
3738
			$activated_manage = $message_code;
3739
			$message_code = 'jetpack-manage';
3740
		}
3741
3742
		switch ( $message_code ) {
3743
		case 'jetpack-manage':
3744
			$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>';
3745
			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...
3746
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
3747
			}
3748
			break;
3749
3750
		}
3751
3752
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
3753
3754
		if ( ! empty( $deactivated_plugins ) ) {
3755
			$deactivated_plugins = explode( ',', $deactivated_plugins );
3756
			$deactivated_titles  = array();
3757
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
3758
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
3759
					continue;
3760
				}
3761
3762
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
3763
			}
3764
3765
			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...
3766
				if ( $this->message ) {
3767
					$this->message .= "<br /><br />\n";
3768
				}
3769
3770
				$this->message .= wp_sprintf(
3771
					_n(
3772
						'Jetpack contains the most recent version of the old %l plugin.',
3773
						'Jetpack contains the most recent versions of the old %l plugins.',
3774
						count( $deactivated_titles ),
3775
						'jetpack'
3776
					),
3777
					$deactivated_titles
3778
				);
3779
3780
				$this->message .= "<br />\n";
3781
3782
				$this->message .= _n(
3783
					'The old version has been deactivated and can be removed from your site.',
3784
					'The old versions have been deactivated and can be removed from your site.',
3785
					count( $deactivated_titles ),
3786
					'jetpack'
3787
				);
3788
			}
3789
		}
3790
3791
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
3792
3793
		if ( $this->message || $this->error || $this->privacy_checks || $this->can_display_jetpack_manage_notice() ) {
3794
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
3795
		}
3796
3797 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
3798
			/**
3799
			 * Fires when a module configuration page is loaded.
3800
			 * The dynamic part of the hook is the configure parameter from the URL.
3801
			 *
3802
			 * @since 1.1.0
3803
			 */
3804
			do_action( 'jetpack_module_configuration_load_' . $_GET['configure'] );
3805
		}
3806
3807
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
3808
	}
3809
3810
	function admin_notices() {
3811
3812
		if ( $this->error ) {
3813
?>
3814
<div id="message" class="jetpack-message jetpack-err">
3815
	<div class="squeezer">
3816
		<h2><?php echo wp_kses( $this->error, array( 'a' => array( 'href' => array() ), 'small' => true, 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
3817
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
3818
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
3819
<?php	endif; ?>
3820
	</div>
3821
</div>
3822
<?php
3823
		}
3824
3825
		if ( $this->message ) {
3826
?>
3827
<div id="message" class="jetpack-message">
3828
	<div class="squeezer">
3829
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
3830
	</div>
3831
</div>
3832
<?php
3833
		}
3834
3835
		if ( $this->privacy_checks ) :
3836
			$module_names = $module_slugs = array();
3837
3838
			$privacy_checks = explode( ',', $this->privacy_checks );
3839
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
3840
			foreach ( $privacy_checks as $module_slug ) {
3841
				$module = Jetpack::get_module( $module_slug );
3842
				if ( ! $module ) {
3843
					continue;
3844
				}
3845
3846
				$module_slugs[] = $module_slug;
3847
				$module_names[] = "<strong>{$module['name']}</strong>";
3848
			}
3849
3850
			$module_slugs = join( ',', $module_slugs );
3851
?>
3852
<div id="message" class="jetpack-message jetpack-err">
3853
	<div class="squeezer">
3854
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
3855
		<p><?php
3856
			echo wp_kses(
3857
				wptexturize(
3858
					wp_sprintf(
3859
						_nx(
3860
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
3861
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
3862
							count( $privacy_checks ),
3863
							'%l = list of Jetpack module/feature names',
3864
							'jetpack'
3865
						),
3866
						$module_names
3867
					)
3868
				),
3869
				array( 'strong' => true )
3870
			);
3871
3872
			echo "\n<br />\n";
3873
3874
			echo wp_kses(
3875
				sprintf(
3876
					_nx(
3877
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
3878
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
3879
						count( $privacy_checks ),
3880
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
3881
						'jetpack'
3882
					),
3883
					wp_nonce_url(
3884
						Jetpack::admin_url(
3885
							array(
3886
								'page'   => 'jetpack',
3887
								'action' => 'deactivate',
3888
								'module' => urlencode( $module_slugs ),
3889
							)
3890
						),
3891
						"jetpack_deactivate-$module_slugs"
3892
					),
3893
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
3894
				),
3895
				array( 'a' => array( 'href' => true, 'title' => true ) )
3896
			);
3897
		?></p>
3898
	</div>
3899
</div>
3900
<?php endif;
3901
	// only display the notice if the other stuff is not there
3902
	if( $this->can_display_jetpack_manage_notice() && !  $this->error && ! $this->message && ! $this->privacy_checks ) {
3903
		if( isset( $_GET['page'] ) && 'jetpack' != $_GET['page'] )
3904
			$this->opt_in_jetpack_manage_notice();
3905
		}
3906
	}
3907
3908
	/**
3909
	 * Record a stat for later output.  This will only currently output in the admin_footer.
3910
	 */
3911
	function stat( $group, $detail ) {
3912
		if ( ! isset( $this->stats[ $group ] ) )
3913
			$this->stats[ $group ] = array();
3914
		$this->stats[ $group ][] = $detail;
3915
	}
3916
3917
	/**
3918
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
3919
	 */
3920
	function do_stats( $method = '' ) {
3921
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
3922
			foreach ( $this->stats as $group => $stats ) {
3923
				if ( is_array( $stats ) && count( $stats ) ) {
3924
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
3925
					if ( 'server_side' === $method ) {
3926
						self::do_server_side_stat( $args );
3927
					} else {
3928
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
3929
					}
3930
				}
3931
				unset( $this->stats[ $group ] );
3932
			}
3933
		}
3934
	}
3935
3936
	/**
3937
	 * Runs stats code for a one-off, server-side.
3938
	 *
3939
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
3940
	 *
3941
	 * @return bool If it worked.
3942
	 */
3943
	static function do_server_side_stat( $args ) {
3944
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
3945
		if ( is_wp_error( $response ) )
3946
			return false;
3947
3948
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
3949
			return false;
3950
3951
		return true;
3952
	}
3953
3954
	/**
3955
	 * Builds the stats url.
3956
	 *
3957
	 * @param $args array|string The arguments to append to the URL.
3958
	 *
3959
	 * @return string The URL to be pinged.
3960
	 */
3961
	static function build_stats_url( $args ) {
3962
		$defaults = array(
3963
			'v'    => 'wpcom2',
3964
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
3965
		);
3966
		$args     = wp_parse_args( $args, $defaults );
3967
		/**
3968
		 * Filter the URL used as the Stats tracking pixel.
3969
		 *
3970
		 * @since 2.3.2
3971
		 *
3972
		 * @param string $url Base URL used as the Stats tracking pixel.
3973
		 */
3974
		$base_url = apply_filters(
3975
			'jetpack_stats_base_url',
3976
			set_url_scheme( 'http://pixel.wp.com/g.gif' )
3977
		);
3978
		$url      = add_query_arg( $args, $base_url );
3979
		return $url;
3980
	}
3981
3982
	function translate_current_user_to_role() {
3983
		foreach ( $this->capability_translations as $role => $cap ) {
3984
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
3985
				return $role;
3986
			}
3987
		}
3988
3989
		return false;
3990
	}
3991
3992
	function translate_role_to_cap( $role ) {
3993
		if ( ! isset( $this->capability_translations[$role] ) ) {
3994
			return false;
3995
		}
3996
3997
		return $this->capability_translations[$role];
3998
	}
3999
4000
	function sign_role( $role ) {
4001
		if ( ! $user_id = (int) get_current_user_id() ) {
4002
			return false;
4003
		}
4004
4005
		$token = Jetpack_Data::get_access_token();
4006
		if ( ! $token || is_wp_error( $token ) ) {
4007
			return false;
4008
		}
4009
4010
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4011
	}
4012
4013
4014
	/**
4015
	 * Builds a URL to the Jetpack connection auth page
4016
	 *
4017
	 * @since 3.9.5
4018
	 *
4019
	 * @param bool $raw If true, URL will not be escaped.
4020
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4021
	 *                              If string, will be a custom redirect.
4022
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4023
	 *
4024
	 * @return string Connect URL
4025
	 */
4026
	function build_connect_url( $raw = false, $redirect = false, $from = false ) {
4027
		if ( ! Jetpack_Options::get_option( 'blog_token' ) || ! Jetpack_Options::get_option( 'id' ) ) {
4028
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4029
			if( is_network_admin() ) {
4030
			    $url = add_query_arg( 'is_multisite', network_admin_url(
4031
			    'admin.php?page=jetpack-settings' ), $url );
4032
			}
4033
		} else {
4034
			require_once JETPACK__GLOTPRESS_LOCALES_PATH;
4035
			$role = $this->translate_current_user_to_role();
4036
			$signed_role = $this->sign_role( $role );
4037
4038
			$user = wp_get_current_user();
4039
4040
			$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4041
			$redirect = $redirect
4042
				? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4043
				: $jetpack_admin_page;
4044
4045
			$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4046
4047
			if( isset( $_REQUEST['is_multisite'] ) ) {
4048
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4049
			}
4050
4051
			$secrets = Jetpack::init()->generate_secrets( 'authorize' );
4052
			@list( $secret ) = explode( ':', $secrets );
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...
4053
			
4054
			$site_icon = ( function_exists( 'has_site_icon') && has_site_icon() )
4055
				? get_site_icon_url()
4056
				: false;
4057
4058
			$args = urlencode_deep(
4059
				array(
4060
					'response_type' => 'code',
4061
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4062
					'redirect_uri'  => add_query_arg(
4063
						array(
4064
							'action'   => 'authorize',
4065
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4066
							'redirect' => urlencode( $redirect ),
4067
						),
4068
						esc_url( admin_url( 'admin.php?page=jetpack' ) )
4069
					),
4070
					'state'         => $user->ID,
4071
					'scope'         => $signed_role,
4072
					'user_email'    => $user->user_email,
4073
					'user_login'    => $user->user_login,
4074
					'is_active'     => Jetpack::is_active(),
4075
					'jp_version'    => JETPACK__VERSION,
4076
					'auth_type'     => 'calypso',
4077
					'secret'        => $secret,
4078
					'locale'        => isset( $gp_locale->slug ) ? $gp_locale->slug : '',
4079
					'blogname'      => get_option( 'blogname' ),
4080
					'site_url'      => site_url(),
4081
					'home_url'      => home_url(),
4082
					'site_icon'     => $site_icon,
4083
				)
4084
			);
4085
4086
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4087
		}
4088
4089
		if ( $from ) {
4090
			$url = add_query_arg( 'from', $from, $url );
4091
		}
4092
4093
		if ( isset( $_GET['calypso_env'] ) ) {
4094
			$url = add_query_arg( 'calypso_env', sanitize_key( $_GET['calypso_env'] ), $url );
4095
		}
4096
4097
		return $raw ? $url : esc_url( $url );
4098
	}
4099
4100
	function build_reconnect_url( $raw = false ) {
4101
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4102
		return $raw ? $url : esc_url( $url );
4103
	}
4104
4105
	public static function admin_url( $args = null ) {
4106
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4107
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4108
		return $url;
4109
	}
4110
4111
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4112
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4113
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4114
	}
4115
4116
	function dismiss_jetpack_notice() {
4117
4118
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4119
			return;
4120
		}
4121
4122
		switch( $_GET['jetpack-notice'] ) {
4123
			case 'dismiss':
4124
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4125
4126
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4127
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4128
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4129
				}
4130
				break;
4131 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...
4132
4133
				if ( check_admin_referer( 'jetpack_manage_banner_opt_out' ) ) {
4134
					// Don't show the banner again
4135
4136
					Jetpack_Options::update_option( 'dismissed_manage_banner', true );
4137
					// redirect back to the page that had the notice
4138
					if ( wp_get_referer() ) {
4139
						wp_safe_redirect( wp_get_referer() );
4140
					} else {
4141
						// Take me to Jetpack
4142
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4143
					}
4144
				}
4145
				break;
4146 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...
4147
4148
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4149
					// Don't show the banner again
4150
4151
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4152
					// redirect back to the page that had the notice
4153
					if ( wp_get_referer() ) {
4154
						wp_safe_redirect( wp_get_referer() );
4155
					} else {
4156
						// Take me to Jetpack
4157
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4158
					}
4159
				}
4160
				break;
4161
			case 'jetpack-manage-opt-in':
4162
				if ( check_admin_referer( 'jetpack_manage_banner_opt_in' ) ) {
4163
					// This makes sure that we are redirect to jetpack home so that we can see the Success Message.
4164
4165
					$redirection_url = Jetpack::admin_url();
4166
					remove_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4167
4168
					// Don't redirect form the Jetpack Setting Page
4169
					$referer_parsed = parse_url ( wp_get_referer() );
4170
					// check that we do have a wp_get_referer and the query paramater is set orderwise go to the Jetpack Home
4171
					if ( isset( $referer_parsed['query'] ) && false !== strpos( $referer_parsed['query'], 'page=jetpack_modules' ) ) {
4172
						// Take the user to Jetpack home except when on the setting page
4173
						$redirection_url = wp_get_referer();
4174
						add_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4175
					}
4176
					// Also update the JSON API FULL MANAGEMENT Option
4177
					Jetpack::activate_module( 'manage', false, false );
4178
4179
					// Special Message when option in.
4180
					Jetpack::state( 'optin-manage', 'true' );
4181
					// Activate the Module if not activated already
4182
4183
					// Redirect properly
4184
					wp_safe_redirect( $redirection_url );
4185
4186
				}
4187
				break;
4188
		}
4189
	}
4190
4191
	function debugger_page() {
4192
		nocache_headers();
4193
		if ( ! current_user_can( 'manage_options' ) ) {
4194
			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...
4195
		}
4196
		Jetpack_Debugger::jetpack_debug_display_handler();
4197
		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...
4198
	}
4199
4200
	public static function admin_screen_configure_module( $module_id ) {
4201
4202
		// User that doesn't have 'jetpack_configure_modules' will never end up here since Jetpack Landing Page woun't let them.
4203
		if ( ! in_array( $module_id, Jetpack::get_active_modules() ) && current_user_can( 'manage_options' ) ) {
4204
			if ( has_action( 'display_activate_module_setting_' . $module_id ) ) {
4205
				/**
4206
				 * Fires to diplay a custom module activation screen.
4207
				 *
4208
				 * To add a module actionation screen use Jetpack::module_configuration_activation_screen method.
4209
				 * Example: Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
4210
				 *
4211
				 * @module manage
4212
				 *
4213
				 * @since 3.8.0
4214
				 *
4215
				 * @param int $module_id Module ID.
4216
				 */
4217
				do_action( 'display_activate_module_setting_' . $module_id );
4218
			} else {
4219
				self::display_activate_module_link( $module_id );
4220
			}
4221
4222
			return false;
4223
		} ?>
4224
4225
		<div id="jp-settings-screen" style="position: relative">
4226
			<h3>
4227
			<?php
4228
				$module = Jetpack::get_module( $module_id );
4229
				echo '<a href="' . Jetpack::admin_url( 'page=jetpack_modules' ) . '">' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</a> &rarr; ';
4230
				printf( __( 'Configure %s', 'jetpack' ), $module['name'] );
4231
			?>
4232
			</h3>
4233
			<?php
4234
				/**
4235
				 * Fires within the displayed message when a feature configuation is updated.
4236
				 *
4237
				 * @since 3.4.0
4238
				 *
4239
				 * @param int $module_id Module ID.
4240
				 */
4241
				do_action( 'jetpack_notices_update_settings', $module_id );
4242
				/**
4243
				 * Fires when a feature configuation screen is loaded.
4244
				 * The dynamic part of the hook, $module_id, is the module ID.
4245
				 *
4246
				 * @since 1.1.0
4247
				 */
4248
				do_action( 'jetpack_module_configuration_screen_' . $module_id );
4249
			?>
4250
		</div><?php
4251
	}
4252
4253
	/**
4254
	 * Display link to activate the module to see the settings screen.
4255
	 * @param  string $module_id
4256
	 * @return null
4257
	 */
4258
	public static function display_activate_module_link( $module_id ) {
4259
4260
		$info =  Jetpack::get_module( $module_id );
4261
		$extra = '';
4262
		$activate_url = wp_nonce_url(
4263
				Jetpack::admin_url(
4264
					array(
4265
						'page'   => 'jetpack',
4266
						'action' => 'activate',
4267
						'module' => $module_id,
4268
					)
4269
				),
4270
				"jetpack_activate-$module_id"
4271
			);
4272
4273
		?>
4274
4275
		<div class="wrap configure-module">
4276
			<div id="jp-settings-screen">
4277
				<?php
4278
				if ( $module_id == 'json-api' ) {
4279
4280
					$info['name'] = esc_html__( 'Activate Site Management and JSON API', 'jetpack' );
4281
4282
					$activate_url = Jetpack::init()->opt_in_jetpack_manage_url();
4283
4284
					$info['description'] = sprintf( __( 'Manage your multiple Jetpack sites from our centralized dashboard at wordpress.com/sites. <a href="%s" target="_blank">Learn more</a>.', 'jetpack' ), 'https://jetpack.com/support/site-management' );
4285
4286
					// $extra = __( 'To use Site Management, you need to first activate JSON API to allow remote management of your site. ', 'jetpack' );
4287
				} ?>
4288
4289
				<h3><?php echo esc_html( $info['name'] ); ?></h3>
4290
				<div class="narrow">
4291
					<p><?php echo  $info['description']; ?></p>
4292
					<?php if( $extra ) { ?>
4293
					<p><?php echo esc_html( $extra ); ?></p>
4294
					<?php } ?>
4295
					<p>
4296
						<?php
4297
						if( wp_get_referer() ) {
4298
							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() );
4299
						} else {
4300
							printf( __( '<a class="button-primary" href="%s">Activate Now</a>', 'jetpack' ) , $activate_url  );
4301
						} ?>
4302
					</p>
4303
				</div>
4304
4305
			</div>
4306
		</div>
4307
4308
		<?php
4309
	}
4310
4311
	public static function sort_modules( $a, $b ) {
4312
		if ( $a['sort'] == $b['sort'] )
4313
			return 0;
4314
4315
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4316
	}
4317
4318 View Code Duplication
	function sync_reindex_trigger() {
4319
		if ( $this->current_user_is_connection_owner() && current_user_can( 'manage_options' ) ) {
4320
			echo json_encode( $this->sync->reindex_trigger() );
4321
		} else {
4322
			echo '{"status":"ERROR"}';
4323
		}
4324
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sync_reindex_trigger() contains an exit expression.

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

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

Loading history...
4325
	}
4326
4327 View Code Duplication
	function sync_reindex_status(){
4328
		if ( $this->current_user_is_connection_owner() && current_user_can( 'manage_options' ) ) {
4329
			echo json_encode( $this->sync->reindex_status() );
4330
		} else {
4331
			echo '{"status":"ERROR"}';
4332
		}
4333
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sync_reindex_status() contains an exit expression.

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

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

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