Completed
Push — update/akismet-caching ( 37313d...6cd298 )
by
unknown
19:58 queued 10:09
created

Jetpack   F

Complexity

Total Complexity 1154

Size/Duplication

Total Lines 7285
Duplicated Lines 3.8 %

Coupling/Cohesion

Components 5
Dependencies 25

Importance

Changes 0
Metric Value
dl 277
loc 7285
rs 0.8
c 0
b 0
f 0
wmc 1154
lcom 5
cbo 25

243 Methods

Rating   Name   Duplication   Size   Complexity  
A jetpack_menu_order() 15 15 4
A admin_head() 3 5 4
A admin_banner_styles() 0 21 4
A plugin_action_links() 0 15 4
A admin_jetpack_manage_notice() 0 30 4
A opt_out_jetpack_manage_url() 0 4 1
A opt_in_jetpack_manage_url() 0 3 1
A opt_in_jetpack_manage_notice() 0 10 1
A can_display_jetpack_manage_notice() 0 22 4
F admin_page_load() 25 275 60
C admin_notices() 0 97 13
A stat() 0 5 2
B do_stats() 0 15 7
A do_server_side_stat() 0 10 3
A build_stats_url() 0 20 1
A translate_current_user_to_role() 0 9 4
A translate_user_to_role() 0 9 4
A translate_role_to_cap() 0 7 2
A sign_role() 0 16 5
F build_connect_url() 0 121 20
A apply_activation_source_to_args() 0 11 3
A build_reconnect_url() 0 4 2
A admin_url() 0 5 1
A nonce_url_no_esc() 0 4 1
C dismiss_jetpack_notice() 30 74 15
A init() 0 9 2
B plugin_upgrade() 0 43 8
A upgrade_on_load() 0 27 5
A activate_manage() 0 7 4
B update_active_modules() 0 54 5
A delete_active_modules() 0 3 1
F __construct() 17 193 25
A point_edit_post_links_to_calypso() 0 28 3
A point_edit_comment_links_to_calypso() 0 8 1
A jetpack_track_last_sync_callback() 0 22 5
A jetpack_connection_banner_callback() 0 10 2
B jetpack_admin_ajax_tracks_callback() 0 23 8
C jetpack_jitm_ajax_callback() 18 57 12
A push_stats() 0 5 2
D jetpack_custom_caps() 0 66 20
A require_jetpack_authentication() 0 11 2
A plugin_textdomain() 0 4 1
B register_assets() 10 79 8
C guess_locale_from_lang() 39 39 12
A get_locale() 0 9 2
A devicepx() 0 5 3
A jetpack_main_network_site_option() 0 3 1
A network_name() 0 4 1
A network_allow_new_registrations() 0 3 2
A network_add_new_users() 0 3 1
A network_site_upload_space() 0 4 2
A network_upload_file_types() 0 3 1
A network_max_upload_file_size() 0 4 1
A network_enable_administration_menus() 0 3 1
A maybe_clear_other_linked_admins_transient() 0 8 5
B get_other_linked_admins() 0 28 7
A is_main_network_option() 0 4 1
A is_multisite() 0 3 1
A is_multi_network() 0 15 3
A update_jetpack_main_network_site_option() 0 3 1
A update_jetpack_network_settings() 0 4 1
A is_single_user_site() 4 9 2
A file_system_write_access() 0 20 4
A is_version_controlled() 0 4 1
A featured_images_enabled() 0 4 2
A get_avatar_url() 0 8 1
A get_updates() 6 17 6
A get_update_details() 0 8 1
A refresh_update_data() 0 4 1
A refresh_theme_data() 0 3 1
A is_active() 0 3 1
B refresh_active_plan_from_wpcom() 0 21 6
C get_active_plan() 14 85 11
B active_plan_supports() 0 25 6
A is_development_mode() 0 21 3
A is_onboarding() 0 3 1
B show_development_mode_notice() 0 40 8
A is_development_version() 0 15 1
A is_user_connected() 0 8 3
A get_connected_user_data() 0 24 4
A get_connected_user_email() 14 14 3
A get_master_user_email() 0 7 2
A current_user_is_connection_owner() 0 4 4
A current_user_ip() 0 20 5
A extra_oembed_providers() 0 9 1
A user_role_change() 0 4 1
D load_modules() 6 89 17
A check_rest_api_compat() 0 16 3
A get_active_plugins() 0 16 3
A get_parsed_plugin_data() 0 21 3
A get_parsed_theme_data() 0 22 3
A is_plugin_active() 0 3 1
B check_open_graph() 0 27 7
A check_twitter_tags() 0 24 5
A submit_security_report() 0 3 1
A get_option_names() 0 3 1
A get_option() 0 3 1
A update_option() 0 4 1
A update_options() 0 4 1
A delete_option() 0 4 1
A update_user_token() 0 14 3
B glob_php() 0 29 7
C activate_new_modules() 6 58 11
C get_available_modules() 0 57 13
B get_default_modules() 0 31 6
B handle_deprecated_modules() 0 26 8
B filter_default_modules() 0 24 6
A get_module_slug() 0 3 1
A get_module_path() 0 3 1
D get_module() 12 91 11
B get_file_data() 0 28 6
A translate_module_tag() 0 3 1
A get_i18n_data_json() 0 18 5
A setup_wp_i18n_locale_data() 0 10 2
A get_translated_modules() 0 13 4
B get_active_modules() 0 32 7
A is_module_active() 0 3 1
A is_module() 0 3 2
A catch_errors() 0 13 2
A catch_errors_on_shutdown() 0 3 1
A alias_directories() 0 8 1
F activate_default_modules() 21 138 22
F activate_module() 8 97 18
A activate_module_actions() 0 3 1
A deactivate_module() 8 27 2
A enable_module_configurable() 0 4 1
A module_configuration_url() 0 4 1
A module_configuration_load() 0 4 1
A module_configuration_head() 0 4 1
A module_configuration_screen() 0 4 1
A module_configuration_activation_screen() 0 4 1
B bail_on_activation() 0 40 5
A plugin_activation() 0 17 3
C get_activation_source() 0 49 14
A do_version_bump() 0 7 2
A plugin_initialize() 6 17 3
A plugin_deactivation() 0 9 2
B disconnect() 0 61 6
A unlink_user() 0 31 5
B try_registration() 0 28 7
A log() 0 40 3
B get_log() 0 36 11
A log_settings_change() 0 7 2
A get_stat_data() 0 14 3
A get_additional_stat_data() 0 11 2
A get_site_user_count() 5 15 4
C admin_init() 0 49 13
A admin_body_class() 0 8 2
A add_jetpack_pagestyles() 0 3 1
A prepare_manage_jetpack_notice() 0 5 1
A manage_activate_screen() 0 3 1
B throw_error_on_activate_plugin() 0 24 7
A intercept_plugin_error_scrape_init() 0 3 1
A intercept_plugin_error_scrape() 0 11 4
A add_remote_request_handlers() 0 4 1
B remote_request_handlers() 0 40 7
F upload_handler() 0 122 24
A admin_help() 0 41 2
A admin_menu_css() 0 3 1
A admin_menu_order() 0 3 1
A admin_screen_configure_module() 0 51 4
A display_activate_module_link() 0 52 4
A sort_modules() 0 6 3
A ajax_recheck_ssl() 0 8 1
A api_url() 0 3 1
A fix_url_for_bad_hosts() 0 16 4
A create_onboarding_token() 0 8 2
A invalidate_onboarding_token() 0 3 1
A validate_onboarding_token_action() 0 18 3
B permit_ssl() 0 42 11
A alert_auto_ssl_fail() 0 49 2
A xmlrpc_api_url() 0 4 1
A generate_secrets() 0 26 4
A get_secrets() 0 15 3
A delete_secrets() 0 8 2
A get_remote_query_timeout_limit() 0 4 1
A get_max_execution_time() 0 9 2
A set_min_time_limit() 0 8 2
C validate_remote_register_response() 0 40 13
C register() 10 104 12
A maybe_set_version_option() 0 14 3
A load_xml_rpc_client() 0 4 1
A reset_saved_auth_state() 0 4 1
F verify_xml_rpc_signature() 0 130 38
B authenticate_jetpack() 0 23 6
C wp_rest_authenticate() 0 75 16
A wp_rest_authentication_errors() 0 6 2
A add_nonce() 0 38 3
A xmlrpc_methods() 0 4 1
A public_xmlrpc_methods() 0 6 2
A jetpack_getOptions() 0 40 3
A xmlrpc_options() 0 18 2
A clean_nonces() 0 21 4
D state() 0 50 18
A restate() 0 3 1
B check_privacy() 0 35 6
C xmlrpc_async_call() 0 42 12
A staticize_subdomain() 0 35 4
A login_form_json_api_authorization() 0 9 1
A post_login_form_to_signed_url() 0 13 5
A preserve_action_in_login_form_for_json_api_authorization() 0 4 1
A store_json_api_authorization_token() 0 6 1
A allow_wpcom_public_api_domain() 0 4 1
A allow_wpcom_environments() 0 7 1
A add_token_to_login_redirect_json_api_authorization() 0 12 1
F verify_json_api_authorization_request() 0 93 21
A login_message_json_api_authorization() 0 6 1
A get_content_width() 0 11 2
A get_cloud_site_options() 0 16 2
A check_identity_crisis() 0 7 4
B is_staging_site() 0 59 10
C validate_sync_error_idc_option() 0 44 13
A normalize_url_protocol_agnostic() 0 10 4
A get_sync_error_idc_option() 0 33 5
A sync_idc_optin() 0 18 3
B maybe_min_asset() 0 42 8
A set_suffix_on_min() 0 16 5
C maybe_inline_style() 0 50 13
A load_view() 0 11 2
B deprecated_hooks() 0 62 6
B absolutize_css_urls() 0 44 9
B implode_frontend_css() 0 52 9
A concat_remove_style_loader_tag() 0 10 4
B jetpack_check_heartbeat_data() 0 53 10
A get_jetpack_options_for_reset() 0 3 1
A jumpstart_has_updated_module_option() 0 21 3
A build_raw_urls() 0 6 1
B dns_prefetch() 0 17 7
A wp_dashboard_setup() 0 24 5
A get_user_option_meta_box_order_dashboard() 0 25 5
A dashboard_widget() 0 8 1
B dashboard_widget_footer() 0 33 7
A get_jp_emblem() 0 3 1
A jetpack_icon_user_connected() 0 4 1
A jetpack_show_user_connected_icon() 0 12 3
A jetpack_user_col_style() 0 17 3
B is_akismet_active() 0 35 10
A is_function_in_backtrace() 0 23 5
A get_file_url_for_environment() 0 7 3
A is_rewind_enabled() 0 19 6
A jetpack_tos_agreed() 0 3 2
A handle_post_authorization_actions() 0 29 4
A show_backups_ui() 0 10 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
Options:
5
jetpack_options (array)
6
	An array of options.
7
	@see Jetpack_Options::get_option_names()
8
9
jetpack_register (string)
10
	Temporary verification secrets.
11
12
jetpack_activated (int)
13
	1: the plugin was activated normally
14
	2: the plugin was activated on this site because of a network-wide activation
15
	3: the plugin was auto-installed
16
	4: the plugin was manually disconnected (but is still installed)
17
18
jetpack_active_modules (array)
19
	Array of active module slugs.
20
21
jetpack_do_activate (bool)
22
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
23
*/
24
25
require_once( JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php' );
26
27
class Jetpack {
28
	public $xmlrpc_server = null;
29
30
	private $xmlrpc_verification = null;
31
	private $rest_authentication_status = null;
32
33
	public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA']
34
35
	/**
36
	 * @var array The handles of styles that are concatenated into jetpack.css.
37
	 *
38
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
39
	 */
40
	public $concatenated_style_handles = array(
41
		'jetpack-carousel',
42
		'grunion.css',
43
		'the-neverending-homepage',
44
		'jetpack_likes',
45
		'jetpack_related-posts',
46
		'sharedaddy',
47
		'jetpack-slideshow',
48
		'presentations',
49
		'quiz',
50
		'jetpack-subscriptions',
51
		'jetpack-responsive-videos-style',
52
		'jetpack-social-menu',
53
		'tiled-gallery',
54
		'jetpack_display_posts_widget',
55
		'gravatar-profile-widget',
56
		'goodreads-widget',
57
		'jetpack_social_media_icons_widget',
58
		'jetpack-top-posts-widget',
59
		'jetpack_image_widget',
60
		'jetpack-my-community-widget',
61
		'jetpack-authors-widget',
62
		'wordads',
63
		'eu-cookie-law-style',
64
		'flickr-widget-style',
65
		'jetpack-search-widget',
66
		'jetpack-simple-payments-widget-style',
67
		'jetpack-widget-social-icons-styles',
68
	);
69
70
	/**
71
	 * Contains all assets that have had their URL rewritten to minified versions.
72
	 *
73
	 * @var array
74
	 */
75
	static $min_assets = array();
76
77
	public $plugins_to_deactivate = array(
78
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
79
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
80
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
81
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
82
		'after-the-deadline'  => array( 'after-the-deadline/after-the-deadline.php', 'After The Deadline' ),
83
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
84
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
85
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
86
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
87
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
88
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
89
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
90
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
91
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
92
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' )
93
	);
94
95
	static $capability_translations = array(
96
		'administrator' => 'manage_options',
97
		'editor'        => 'edit_others_posts',
98
		'author'        => 'publish_posts',
99
		'contributor'   => 'edit_posts',
100
		'subscriber'    => 'read',
101
	);
102
103
	/**
104
	 * Map of modules that have conflicts with plugins and should not be auto-activated
105
	 * if the plugins are active.  Used by filter_default_modules
106
	 *
107
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
108
	 * change `module-slug` and add this to your plugin:
109
	 *
110
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
111
	 * function my_jetpack_get_default_modules( $modules ) {
112
	 *     return array_diff( $modules, array( 'module-slug' ) );
113
	 * }
114
	 *
115
	 * @var array
116
	 */
117
	private $conflicting_plugins = array(
118
		'comments'          => array(
119
			'Intense Debate'                       => 'intensedebate/intensedebate.php',
120
			'Disqus'                               => 'disqus-comment-system/disqus.php',
121
			'Livefyre'                             => 'livefyre-comments/livefyre.php',
122
			'Comments Evolved for WordPress'       => 'gplus-comments/comments-evolved.php',
123
			'Google+ Comments'                     => 'google-plus-comments/google-plus-comments.php',
124
			'WP-SpamShield Anti-Spam'              => 'wp-spamshield/wp-spamshield.php',
125
		),
126
		'comment-likes' => array(
127
			'Epoch'                                => 'epoch/plugincore.php',
128
		),
129
		'contact-form'      => array(
130
			'Contact Form 7'                       => 'contact-form-7/wp-contact-form-7.php',
131
			'Gravity Forms'                        => 'gravityforms/gravityforms.php',
132
			'Contact Form Plugin'                  => 'contact-form-plugin/contact_form.php',
133
			'Easy Contact Forms'                   => 'easy-contact-forms/easy-contact-forms.php',
134
			'Fast Secure Contact Form'             => 'si-contact-form/si-contact-form.php',
135
			'Ninja Forms'                          => 'ninja-forms/ninja-forms.php',
136
		),
137
		'minileven'         => array(
138
			'WPtouch'                              => 'wptouch/wptouch.php',
139
		),
140
		'latex'             => array(
141
			'LaTeX for WordPress'                  => 'latex/latex.php',
142
			'Youngwhans Simple Latex'              => 'youngwhans-simple-latex/yw-latex.php',
143
			'Easy WP LaTeX'                        => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
144
			'MathJax-LaTeX'                        => 'mathjax-latex/mathjax-latex.php',
145
			'Enable Latex'                         => 'enable-latex/enable-latex.php',
146
			'WP QuickLaTeX'                        => 'wp-quicklatex/wp-quicklatex.php',
147
		),
148
		'protect'           => array(
149
			'Limit Login Attempts'                 => 'limit-login-attempts/limit-login-attempts.php',
150
			'Captcha'                              => 'captcha/captcha.php',
151
			'Brute Force Login Protection'         => 'brute-force-login-protection/brute-force-login-protection.php',
152
			'Login Security Solution'              => 'login-security-solution/login-security-solution.php',
153
			'WPSecureOps Brute Force Protect'      => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
154
			'BulletProof Security'                 => 'bulletproof-security/bulletproof-security.php',
155
			'SiteGuard WP Plugin'                  => 'siteguard/siteguard.php',
156
			'Security-protection'                  => 'security-protection/security-protection.php',
157
			'Login Security'                       => 'login-security/login-security.php',
158
			'Botnet Attack Blocker'                => 'botnet-attack-blocker/botnet-attack-blocker.php',
159
			'Wordfence Security'                   => 'wordfence/wordfence.php',
160
			'All In One WP Security & Firewall'    => 'all-in-one-wp-security-and-firewall/wp-security.php',
161
			'iThemes Security'                     => 'better-wp-security/better-wp-security.php',
162
		),
163
		'random-redirect'   => array(
164
			'Random Redirect 2'                    => 'random-redirect-2/random-redirect.php',
165
		),
166
		'related-posts'     => array(
167
			'YARPP'                                => 'yet-another-related-posts-plugin/yarpp.php',
168
			'WordPress Related Posts'              => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
169
			'nrelate Related Content'              => 'nrelate-related-content/nrelate-related.php',
170
			'Contextual Related Posts'             => 'contextual-related-posts/contextual-related-posts.php',
171
			'Related Posts for WordPress'          => 'microkids-related-posts/microkids-related-posts.php',
172
			'outbrain'                             => 'outbrain/outbrain.php',
173
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
174
			'Sexybookmarks'                        => 'sexybookmarks/shareaholic.php',
175
		),
176
		'sharedaddy'        => array(
177
			'AddThis'                              => 'addthis/addthis_social_widget.php',
178
			'Add To Any'                           => 'add-to-any/add-to-any.php',
179
			'ShareThis'                            => 'share-this/sharethis.php',
180
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
181
		),
182
		'seo-tools' => array(
183
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
184
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
185
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
186
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
187
			'The SEO Framework'                    => 'autodescription/autodescription.php',
188
		),
189
		'verification-tools' => array(
190
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
191
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
192
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
193
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
194
			'The SEO Framework'                    => 'autodescription/autodescription.php',
195
		),
196
		'widget-visibility' => array(
197
			'Widget Logic'                         => 'widget-logic/widget_logic.php',
198
			'Dynamic Widgets'                      => 'dynamic-widgets/dynamic-widgets.php',
199
		),
200
		'sitemaps' => array(
201
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
202
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
203
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
204
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
205
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
206
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
207
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
208
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
209
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
210
			'The SEO Framework'                    => 'autodescription/autodescription.php',
211
			'Sitemap'                              => 'sitemap/sitemap.php',
212
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
213
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
214
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
215
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
216
		),
217
		'lazy-images' => array(
218
			'Lazy Load'              => 'lazy-load/lazy-load.php',
219
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
220
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
221
		),
222
	);
223
224
	/**
225
	 * Plugins for which we turn off our Facebook OG Tags implementation.
226
	 *
227
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
228
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
229
	 *
230
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
231
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
232
	 */
233
	private $open_graph_conflicting_plugins = array(
234
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
235
		                                                         // 2 Click Social Media Buttons
236
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
237
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
238
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
239
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
240
		                                                         // Open Graph Meta Tags by Heateor
241
		'facebook/facebook.php',                                 // Facebook (official plugin)
242
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
243
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
244
		                                                         // Facebook Featured Image & OG Meta Tags
245
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
246
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
247
		                                                         // Facebook Open Graph Meta Tags for WordPress
248
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
249
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
250
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
251
		                                                         // Fedmich's Facebook Open Graph Meta
252
		'network-publisher/networkpub.php',                      // Network Publisher
253
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
254
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
255
		                                                         // NextScripts SNAP
256
		'og-tags/og-tags.php',                                   // OG Tags
257
		'opengraph/opengraph.php',                               // Open Graph
258
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
259
		                                                         // Open Graph Protocol Framework
260
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
261
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
262
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
263
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
264
		'sharepress/sharepress.php',                             // SharePress
265
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
266
		'social-discussions/social-discussions.php',             // Social Discussions
267
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
268
		'socialize/socialize.php',                               // Socialize
269
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
270
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
271
		                                                         // Tweet, Like, Google +1 and Share
272
		'wordbooker/wordbooker.php',                             // Wordbooker
273
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
274
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
275
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
276
		                                                         // WP Facebook Like Send & Open Graph Meta
277
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
278
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
279
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
280
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
281
		'open-graph-metabox/open-graph-metabox.php'              // Open Graph Metabox
282
	);
283
284
	/**
285
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
286
	 */
287
	private $twitter_cards_conflicting_plugins = array(
288
	//	'twitter/twitter.php',                       // The official one handles this on its own.
289
	//	                                             // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
290
		'eewee-twitter-card/index.php',              // Eewee Twitter Card
291
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
292
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
293
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
294
		                                             // Pure Web Brilliant's Social Graph Twitter Cards Extension
295
		'twitter-cards/twitter-cards.php',           // Twitter Cards
296
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
297
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
298
	);
299
300
	/**
301
	 * Message to display in admin_notice
302
	 * @var string
303
	 */
304
	public $message = '';
305
306
	/**
307
	 * Error to display in admin_notice
308
	 * @var string
309
	 */
310
	public $error = '';
311
312
	/**
313
	 * Modules that need more privacy description.
314
	 * @var string
315
	 */
316
	public $privacy_checks = '';
317
318
	/**
319
	 * Stats to record once the page loads
320
	 *
321
	 * @var array
322
	 */
323
	public $stats = array();
324
325
	/**
326
	 * Jetpack_Sync object
327
	 */
328
	public $sync;
329
330
	/**
331
	 * Verified data for JSON authorization request
332
	 */
333
	public $json_api_authorization_request = array();
334
335
	/**
336
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
337
	 */
338
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
339
340
	/**
341
	 * Holds the singleton instance of this class
342
	 * @since 2.3.3
343
	 * @var Jetpack
344
	 */
345
	static $instance = false;
346
347
	/**
348
	 * Singleton
349
	 * @static
350
	 */
351
	public static function init() {
352
		if ( ! self::$instance ) {
353
			self::$instance = new Jetpack;
354
355
			self::$instance->plugin_upgrade();
356
		}
357
358
		return self::$instance;
359
	}
360
361
	/**
362
	 * Must never be called statically
363
	 */
364
	function plugin_upgrade() {
365
		if ( Jetpack::is_active() ) {
366
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
367
			if ( JETPACK__VERSION != $version ) {
368
				// Prevent multiple upgrades at once - only a single process should trigger
369
				// an upgrade to avoid stampedes
370
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
371
					return;
372
				}
373
374
				// Set a short lock to prevent multiple instances of the upgrade
375
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
376
377
				// check which active modules actually exist and remove others from active_modules list
378
				$unfiltered_modules = Jetpack::get_active_modules();
379
				$modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
380
				if ( array_diff( $unfiltered_modules, $modules ) ) {
381
					Jetpack::update_active_modules( $modules );
382
				}
383
384
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
385
386
				// Upgrade to 4.3.0
387
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
388
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
389
				}
390
391
				// Make sure Markdown for posts gets turned back on
392
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
393
					update_option( 'wpcom_publish_posts_with_markdown', true );
394
				}
395
396
				if ( did_action( 'wp_loaded' ) ) {
397
					self::upgrade_on_load();
398
				} else {
399
					add_action(
400
						'wp_loaded',
401
						array( __CLASS__, 'upgrade_on_load' )
402
					);
403
				}
404
			}
405
		}
406
	}
407
408
	/**
409
	 * Runs upgrade routines that need to have modules loaded.
410
	 */
411
	static function upgrade_on_load() {
412
413
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
414
		// This can happen in case Jetpack has been just upgraded and is
415
		// being initialized late during the page load. In this case we wait
416
		// until the next proper admin page load with Jetpack active.
417
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
418
			delete_transient( self::$plugin_upgrade_lock_key );
419
420
			return;
421
		}
422
423
		Jetpack::maybe_set_version_option();
424
425
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
426
			Jetpack_Widget_Conditions::migrate_post_type_rules();
427
		}
428
429
		if (
430
			class_exists( 'Jetpack_Sitemap_Manager' )
431
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
432
		) {
433
			do_action( 'jetpack_sitemaps_purge_data' );
434
		}
435
436
		delete_transient( self::$plugin_upgrade_lock_key );
437
	}
438
439
	static function activate_manage( ) {
440
		if ( did_action( 'init' ) || current_filter() == 'init' ) {
441
			self::activate_module( 'manage', false, false );
442
		} else if ( !  has_action( 'init' , array( __CLASS__, 'activate_manage' ) ) ) {
443
			add_action( 'init', array( __CLASS__, 'activate_manage' ) );
444
		}
445
	}
446
447
	static function update_active_modules( $modules ) {
448
		$current_modules = Jetpack_Options::get_option( 'active_modules', array() );
449
450
		$success = Jetpack_Options::update_option( 'active_modules', array_unique( $modules ) );
451
452
		if ( is_array( $modules ) && is_array( $current_modules ) ) {
453
			$new_active_modules = array_diff( $modules, $current_modules );
454
			foreach( $new_active_modules as $module ) {
455
				/**
456
				 * Fires when a specific module is activated.
457
				 *
458
				 * @since 1.9.0
459
				 *
460
				 * @param string $module Module slug.
461
				 * @param boolean $success whether the module was activated. @since 4.2
462
				 */
463
				do_action( 'jetpack_activate_module', $module, $success );
464
465
				/**
466
				 * Fires when a module is activated.
467
				 * The dynamic part of the filter, $module, is the module slug.
468
				 *
469
				 * @since 1.9.0
470
				 *
471
				 * @param string $module Module slug.
472
				 */
473
				do_action( "jetpack_activate_module_$module", $module );
474
			}
475
476
			$new_deactive_modules = array_diff( $current_modules, $modules );
477
			foreach( $new_deactive_modules as $module ) {
478
				/**
479
				 * Fired after a module has been deactivated.
480
				 *
481
				 * @since 4.2.0
482
				 *
483
				 * @param string $module Module slug.
484
				 * @param boolean $success whether the module was deactivated.
485
				 */
486
				do_action( 'jetpack_deactivate_module', $module, $success );
487
				/**
488
				 * Fires when a module is deactivated.
489
				 * The dynamic part of the filter, $module, is the module slug.
490
				 *
491
				 * @since 1.9.0
492
				 *
493
				 * @param string $module Module slug.
494
				 */
495
				do_action( "jetpack_deactivate_module_$module", $module );
496
			}
497
		}
498
499
		return $success;
500
	}
501
502
	static function delete_active_modules() {
503
		self::update_active_modules( array() );
504
	}
505
506
	/**
507
	 * Constructor.  Initializes WordPress hooks
508
	 */
509
	private function __construct() {
510
		/*
511
		 * Check for and alert any deprecated hooks
512
		 */
513
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
514
515
		/*
516
		 * Enable enhanced handling of previewing sites in Calypso
517
		 */
518
		if ( Jetpack::is_active() ) {
519
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
520
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
521
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
522
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
523
		}
524
525
		/*
526
		 * Load things that should only be in Network Admin.
527
		 *
528
		 * For now blow away everything else until a more full
529
		 * understanding of what is needed at the network level is
530
		 * available
531
		 */
532
		if( is_multisite() ) {
533
			Jetpack_Network::init();
534
		}
535
536
		/**
537
		 * Prepare Gutenberg Editor functionality
538
		 */
539
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
540
		add_action( 'enqueue_block_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_assets' ) );
541
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
542
543
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
544
545
		// Unlink user before deleting the user from .com
546
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
547
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
548
549
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
550
			@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...
551
552
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
553
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
554
555
			$this->require_jetpack_authentication();
556
557
			if ( Jetpack::is_active() ) {
558
				// Hack to preserve $HTTP_RAW_POST_DATA
559
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
560
561
				$signed = $this->verify_xml_rpc_signature();
562 View Code Duplication
				if ( $signed && ! is_wp_error( $signed ) ) {
563
					// The actual API methods.
564
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
565
				} else {
566
					// The jetpack.authorize method should be available for unauthenticated users on a site with an
567
					// active Jetpack connection, so that additional users can link their account.
568
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'authorize_xmlrpc_methods' ) );
569
				}
570 View Code Duplication
			} else {
571
				// The bootstrap API methods.
572
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
573
				$signed = $this->verify_xml_rpc_signature();
574
				if ( $signed && ! is_wp_error( $signed ) ) {
575
					// the jetpack Provision method is available for blog-token-signed requests
576
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'provision_xmlrpc_methods' ) );
577
				}
578
			}
579
580
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
581
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
582
		} elseif (
583
			is_admin() &&
584
			isset( $_POST['action'] ) && (
585
				'jetpack_upload_file' == $_POST['action'] ||
586
				'jetpack_update_file' == $_POST['action']
587
			)
588
		) {
589
			$this->require_jetpack_authentication();
590
			$this->add_remote_request_handlers();
591
		} else {
592
			if ( Jetpack::is_active() ) {
593
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
594
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
595
			}
596
		}
597
598
		if ( Jetpack::is_active() ) {
599
			Jetpack_Heartbeat::init();
600
			if ( Jetpack::is_module_active( 'stats' ) && Jetpack::is_module_active( 'search' ) ) {
601
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
602
				Jetpack_Search_Performance_Logger::init();
603
			}
604
		}
605
606
		add_filter( 'determine_current_user', array( $this, 'wp_rest_authenticate' ) );
607
		add_filter( 'rest_authentication_errors', array( $this, 'wp_rest_authentication_errors' ) );
608
609
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
610
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
611
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
612
		}
613
614
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
615
616
		add_action( 'admin_init', array( $this, 'admin_init' ) );
617
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
618
619
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
620
621
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
622
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
623
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
624
625
		// returns HTTPS support status
626
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
627
628
		// If any module option is updated before Jump Start is dismissed, hide Jump Start.
629
		add_action( 'update_option', array( $this, 'jumpstart_has_updated_module_option' ) );
630
631
		// JITM AJAX callback function
632
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
633
634
		// Universal ajax callback for all tracking events triggered via js
635
		add_action( 'wp_ajax_jetpack_tracks', array( $this, 'jetpack_admin_ajax_tracks_callback' ) );
636
637
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
638
639
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
640
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
641
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
642
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
643
644
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
645
646
		/**
647
		 * These actions run checks to load additional files.
648
		 * They check for external files or plugins, so they need to run as late as possible.
649
		 */
650
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
651
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
652
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
653
654
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
655
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2  );
656
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
657
658
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
659
660
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
661
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
662
663
		// A filter to control all just in time messages
664
		add_filter( 'jetpack_just_in_time_msgs', '__return_true', 9 );
665
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9);
666
667
		// If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
668
		// We should make sure to only do this for front end links.
669
		if ( Jetpack::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
670
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
671
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
672
673
			//we'll override wp_notify_postauthor and wp_notify_moderator pluggable functions
674
			//so they point moderation links on emails to Calypso
675
			jetpack_require_lib( 'functions.wp-notify' );
676
		}
677
678
		// Update the Jetpack plan from API on heartbeats
679
		add_action( 'jetpack_heartbeat', array( $this, 'refresh_active_plan_from_wpcom' ) );
680
681
		/**
682
		 * This is the hack to concatenate all css files into one.
683
		 * For description and reasoning see the implode_frontend_css method
684
		 *
685
		 * Super late priority so we catch all the registered styles
686
		 */
687
		if( !is_admin() ) {
688
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
689
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
690
		}
691
692
		/**
693
		 * These are sync actions that we need to keep track of for jitms
694
		 */
695
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
696
697
		// Actually push the stats on shutdown.
698
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
699
			add_action( 'shutdown', array( $this, 'push_stats' ) );
700
		}
701
	}
702
703
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
704
		$post = get_post( $post_id );
705
706
		if ( empty( $post ) ) {
707
			return $default_url;
708
		}
709
710
		$post_type = $post->post_type;
711
712
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
713
		// https://en.support.wordpress.com/custom-post-types/
714
		$allowed_post_types = array(
715
			'post' => 'post',
716
			'page' => 'page',
717
			'jetpack-portfolio' => 'edit/jetpack-portfolio',
718
			'jetpack-testimonial' => 'edit/jetpack-testimonial',
719
		);
720
721
		if ( ! in_array( $post_type, array_keys( $allowed_post_types ) ) ) {
722
			return $default_url;
723
		}
724
725
		$path_prefix = $allowed_post_types[ $post_type ];
726
727
		$site_slug  = Jetpack::build_raw_urls( get_home_url() );
728
729
		return esc_url( sprintf( 'https://wordpress.com/%s/%s/%d', $path_prefix, $site_slug, $post_id ) );
730
	}
731
732
	function point_edit_comment_links_to_calypso( $url ) {
733
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
734
		wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
735
		return esc_url( sprintf( 'https://wordpress.com/comment/%s/%d',
736
			Jetpack::build_raw_urls( get_home_url() ),
737
			$query_args['amp;c']
738
		) );
739
	}
740
741
	function jetpack_track_last_sync_callback( $params ) {
742
		/**
743
		 * Filter to turn off jitm caching
744
		 *
745
		 * @since 5.4.0
746
		 *
747
		 * @param bool false Whether to cache just in time messages
748
		 */
749
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
750
			return $params;
751
		}
752
753
		if ( is_array( $params ) && isset( $params[0] ) ) {
754
			$option = $params[0];
755
			if ( 'active_plugins' === $option ) {
756
				// use the cache if we can, but not terribly important if it gets evicted
757
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
758
			}
759
		}
760
761
		return $params;
762
	}
763
764
	function jetpack_connection_banner_callback() {
765
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
766
767
		if ( isset( $_REQUEST['dismissBanner'] ) ) {
768
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
769
			wp_send_json_success();
770
		}
771
772
		wp_die();
773
	}
774
775
	function jetpack_admin_ajax_tracks_callback() {
776
		// Check for nonce
777
		if ( ! isset( $_REQUEST['tracksNonce'] ) || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' ) ) {
778
			wp_die( 'Permissions check failed.' );
779
		}
780
781
		if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] )  ) {
782
			wp_die( 'No valid event name or type.' );
783
		}
784
785
		$tracks_data = array();
786
		if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
787
			if ( is_array( $_REQUEST['tracksEventProp'] ) ) {
788
				$tracks_data = $_REQUEST['tracksEventProp'];
789
			} else {
790
				$tracks_data = array( 'clicked' => $_REQUEST['tracksEventProp'] );
791
			}
792
		}
793
794
		JetpackTracking::record_user_event( $_REQUEST['tracksEventName'], $tracks_data );
795
		wp_send_json_success();
796
		wp_die();
797
	}
798
799
	/**
800
	 * The callback for the JITM ajax requests.
801
	 */
802
	function jetpack_jitm_ajax_callback() {
803
		// Check for nonce
804
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
805
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
806
		}
807
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
808
			$module_slug = $_REQUEST['jitmModule'];
809
			Jetpack::log( 'activate', $module_slug );
810
			Jetpack::activate_module( $module_slug, false, false );
811
			Jetpack::state( 'message', 'no_message' );
812
813
			//A Jetpack module is being activated through a JITM, track it
814
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
815
			$this->do_stats( 'server_side' );
816
817
			wp_send_json_success();
818
		}
819
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
820
			// get the hide_jitm options array
821
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
822
			$module_slug = $_REQUEST['jitmModule'];
823
824
			if( ! $jetpack_hide_jitm ) {
825
				$jetpack_hide_jitm = array(
826
					$module_slug => 'hide'
827
				);
828
			} else {
829
				$jetpack_hide_jitm[$module_slug] = 'hide';
830
			}
831
832
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
833
834
			//jitm is being dismissed forever, track it
835
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
836
			$this->do_stats( 'server_side' );
837
838
			wp_send_json_success();
839
		}
840 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
841
			$module_slug = $_REQUEST['jitmModule'];
842
843
			// User went to WordPress.com, track this
844
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
845
			$this->do_stats( 'server_side' );
846
847
			wp_send_json_success();
848
		}
849 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'viewed' == $_REQUEST['jitmActionToTake'] ) {
850
			$track = $_REQUEST['jitmModule'];
851
852
			// User is viewing JITM, track it.
853
			$this->stat( 'jitm', $track . '-viewed-' . JETPACK__VERSION );
854
			$this->do_stats( 'server_side' );
855
856
			wp_send_json_success();
857
		}
858
	}
859
860
	/**
861
	 * If there are any stats that need to be pushed, but haven't been, push them now.
862
	 */
863
	function push_stats() {
864
		if ( ! empty( $this->stats ) ) {
865
			$this->do_stats( 'server_side' );
866
		}
867
	}
868
869
	function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
870
		switch( $cap ) {
871
			case 'jetpack_connect' :
872
			case 'jetpack_reconnect' :
873
				if ( Jetpack::is_development_mode() ) {
874
					$caps = array( 'do_not_allow' );
875
					break;
876
				}
877
				/**
878
				 * Pass through. If it's not development mode, these should match disconnect.
879
				 * Let users disconnect if it's development mode, just in case things glitch.
880
				 */
881
			case 'jetpack_disconnect' :
882
				/**
883
				 * In multisite, can individual site admins manage their own connection?
884
				 *
885
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
886
				 */
887
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
888
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
889
						/**
890
						 * We need to update the option name -- it's terribly unclear which
891
						 * direction the override goes.
892
						 *
893
						 * @todo: Update the option name to `sub-sites-can-manage-own-connections`
894
						 */
895
						$caps = array( 'do_not_allow' );
896
						break;
897
					}
898
				}
899
900
				$caps = array( 'manage_options' );
901
				break;
902
			case 'jetpack_manage_modules' :
903
			case 'jetpack_activate_modules' :
904
			case 'jetpack_deactivate_modules' :
905
				$caps = array( 'manage_options' );
906
				break;
907
			case 'jetpack_configure_modules' :
908
				$caps = array( 'manage_options' );
909
				break;
910
			case 'jetpack_network_admin_page':
911
			case 'jetpack_network_settings_page':
912
				$caps = array( 'manage_network_plugins' );
913
				break;
914
			case 'jetpack_network_sites_page':
915
				$caps = array( 'manage_sites' );
916
				break;
917
			case 'jetpack_admin_page' :
918
				if ( Jetpack::is_development_mode() ) {
919
					$caps = array( 'manage_options' );
920
					break;
921
				} else {
922
					$caps = array( 'read' );
923
				}
924
				break;
925
			case 'jetpack_connect_user' :
926
				if ( Jetpack::is_development_mode() ) {
927
					$caps = array( 'do_not_allow' );
928
					break;
929
				}
930
				$caps = array( 'read' );
931
				break;
932
		}
933
		return $caps;
934
	}
935
936
	function require_jetpack_authentication() {
937
		// Don't let anyone authenticate
938
		$_COOKIE = array();
939
		remove_all_filters( 'authenticate' );
940
		remove_all_actions( 'wp_login_failed' );
941
942
		if ( Jetpack::is_active() ) {
943
			// Allow Jetpack authentication
944
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
945
		}
946
	}
947
948
	/**
949
	 * Load language files
950
	 * @action plugins_loaded
951
	 */
952
	public static function plugin_textdomain() {
953
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
954
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
955
	}
956
957
	/**
958
	 * Register assets for use in various modules and the Jetpack admin page.
959
	 *
960
	 * @uses wp_script_is, wp_register_script, plugins_url
961
	 * @action wp_loaded
962
	 * @return null
963
	 */
964
	public function register_assets() {
965
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
966
			wp_register_script(
967
				'spin',
968
				self::get_file_url_for_environment( '_inc/build/spin.min.js', '_inc/spin.js' ),
969
				false,
970
				'1.3'
971
			);
972
		}
973
974
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
975
			wp_register_script(
976
				'jquery.spin',
977
				self::get_file_url_for_environment( '_inc/build/jquery.spin.min.js', '_inc/jquery.spin.js' ),
978
				array( 'jquery', 'spin' ),
979
				'1.3'
980
			);
981
		}
982
983 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
984
			wp_register_script(
985
				'jetpack-gallery-settings',
986
				self::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
987
				array( 'media-views' ),
988
				'20121225'
989
			);
990
		}
991
992
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
993
			wp_register_script(
994
				'jetpack-twitter-timeline',
995
				self::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
996
				array( 'jquery' ),
997
				'4.0.0',
998
				true
999
			);
1000
		}
1001
1002
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1003
			wp_register_script(
1004
				'jetpack-facebook-embed',
1005
				self::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1006
				array( 'jquery' ),
1007
				null,
1008
				true
1009
			);
1010
1011
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1012
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1013
			if ( ! is_numeric( $fb_app_id ) ) {
1014
				$fb_app_id = '';
1015
			}
1016
			wp_localize_script(
1017
				'jetpack-facebook-embed',
1018
				'jpfbembed',
1019
				array(
1020
					'appid' => $fb_app_id,
1021
					'locale' => $this->get_locale(),
1022
				)
1023
			);
1024
		}
1025
1026
		/**
1027
		 * As jetpack_register_genericons is by default fired off a hook,
1028
		 * the hook may have already fired by this point.
1029
		 * So, let's just trigger it manually.
1030
		 */
1031
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
1032
		jetpack_register_genericons();
1033
1034
		/**
1035
		 * Register the social logos
1036
		 */
1037
		require_once( JETPACK__PLUGIN_DIR . '_inc/social-logos.php' );
1038
		jetpack_register_social_logos();
1039
1040 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
1041
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1042
	}
1043
1044
	/**
1045
	 * Guess locale from language code.
1046
	 *
1047
	 * @param string $lang Language code.
1048
	 * @return string|bool
1049
	 */
1050 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1051
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1052
			return 'en_US';
1053
		}
1054
1055
		if ( ! class_exists( 'GP_Locales' ) ) {
1056
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1057
				return false;
1058
			}
1059
1060
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1061
		}
1062
1063
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1064
			// WP.com: get_locale() returns 'it'
1065
			$locale = GP_Locales::by_slug( $lang );
1066
		} else {
1067
			// Jetpack: get_locale() returns 'it_IT';
1068
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1069
		}
1070
1071
		if ( ! $locale ) {
1072
			return false;
1073
		}
1074
1075
		if ( empty( $locale->facebook_locale ) ) {
1076
			if ( empty( $locale->wp_locale ) ) {
1077
				return false;
1078
			} else {
1079
				// Facebook SDK is smart enough to fall back to en_US if a
1080
				// locale isn't supported. Since supported Facebook locales
1081
				// can fall out of sync, we'll attempt to use the known
1082
				// wp_locale value and rely on said fallback.
1083
				return $locale->wp_locale;
1084
			}
1085
		}
1086
1087
		return $locale->facebook_locale;
1088
	}
1089
1090
	/**
1091
	 * Get the locale.
1092
	 *
1093
	 * @return string|bool
1094
	 */
1095
	function get_locale() {
1096
		$locale = $this->guess_locale_from_lang( get_locale() );
1097
1098
		if ( ! $locale ) {
1099
			$locale = 'en_US';
1100
		}
1101
1102
		return $locale;
1103
	}
1104
1105
	/**
1106
	 * Device Pixels support
1107
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
1108
	 */
1109
	function devicepx() {
1110
		if ( Jetpack::is_active() && ! Jetpack_AMP_Support::is_amp_request() ) {
1111
			wp_enqueue_script( 'devicepx', 'https://s0.wp.com/wp-content/js/devicepx-jetpack.js', array(), gmdate( 'oW' ), true );
1112
		}
1113
	}
1114
1115
	/**
1116
	 * Return the network_site_url so that .com knows what network this site is a part of.
1117
	 * @param  bool $option
1118
	 * @return string
1119
	 */
1120
	public function jetpack_main_network_site_option( $option ) {
1121
		return network_site_url();
1122
	}
1123
	/**
1124
	 * Network Name.
1125
	 */
1126
	static function network_name( $option = null ) {
1127
		global $current_site;
1128
		return $current_site->site_name;
1129
	}
1130
	/**
1131
	 * Does the network allow new user and site registrations.
1132
	 * @return string
1133
	 */
1134
	static function network_allow_new_registrations( $option = null ) {
1135
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
1136
	}
1137
	/**
1138
	 * Does the network allow admins to add new users.
1139
	 * @return boolian
1140
	 */
1141
	static function network_add_new_users( $option = null ) {
1142
		return (bool) get_site_option( 'add_new_users' );
1143
	}
1144
	/**
1145
	 * File upload psace left per site in MB.
1146
	 *  -1 means NO LIMIT.
1147
	 * @return number
1148
	 */
1149
	static function network_site_upload_space( $option = null ) {
1150
		// value in MB
1151
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1152
	}
1153
1154
	/**
1155
	 * Network allowed file types.
1156
	 * @return string
1157
	 */
1158
	static function network_upload_file_types( $option = null ) {
1159
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1160
	}
1161
1162
	/**
1163
	 * Maximum file upload size set by the network.
1164
	 * @return number
1165
	 */
1166
	static function network_max_upload_file_size( $option = null ) {
1167
		// value in KB
1168
		return get_site_option( 'fileupload_maxk', 300 );
1169
	}
1170
1171
	/**
1172
	 * Lets us know if a site allows admins to manage the network.
1173
	 * @return array
1174
	 */
1175
	static function network_enable_administration_menus( $option = null ) {
1176
		return get_site_option( 'menu_items' );
1177
	}
1178
1179
	/**
1180
	 * If a user has been promoted to or demoted from admin, we need to clear the
1181
	 * jetpack_other_linked_admins transient.
1182
	 *
1183
	 * @since 4.3.2
1184
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1185
	 *
1186
	 * @param int    $user_id   The user ID whose role changed.
1187
	 * @param string $role      The new role.
1188
	 * @param array  $old_roles An array of the user's previous roles.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $old_roles not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1189
	 */
1190
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1191
		if ( 'administrator' == $role
1192
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1193
			|| is_null( $old_roles )
1194
		) {
1195
			delete_transient( 'jetpack_other_linked_admins' );
1196
		}
1197
	}
1198
1199
	/**
1200
	 * Checks to see if there are any other users available to become primary
1201
	 * Users must both:
1202
	 * - Be linked to wpcom
1203
	 * - Be an admin
1204
	 *
1205
	 * @return mixed False if no other users are linked, Int if there are.
1206
	 */
1207
	static function get_other_linked_admins() {
1208
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1209
1210
		if ( false === $other_linked_users ) {
1211
			$admins = get_users( array( 'role' => 'administrator' ) );
1212
			if ( count( $admins ) > 1 ) {
1213
				$available = array();
1214
				foreach ( $admins as $admin ) {
1215
					if ( Jetpack::is_user_connected( $admin->ID ) ) {
1216
						$available[] = $admin->ID;
1217
					}
1218
				}
1219
1220
				$count_connected_admins = count( $available );
1221
				if ( count( $available ) > 1 ) {
1222
					$other_linked_users = $count_connected_admins;
1223
				} else {
1224
					$other_linked_users = 0;
1225
				}
1226
			} else {
1227
				$other_linked_users = 0;
1228
			}
1229
1230
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1231
		}
1232
1233
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1234
	}
1235
1236
	/**
1237
	 * Return whether we are dealing with a multi network setup or not.
1238
	 * The reason we are type casting this is because we want to avoid the situation where
1239
	 * the result is false since when is_main_network_option return false it cases
1240
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1241
	 * database which could be set to anything as opposed to what this function returns.
1242
	 * @param  bool  $option
1243
	 *
1244
	 * @return boolean
1245
	 */
1246
	public function is_main_network_option( $option ) {
1247
		// return '1' or ''
1248
		return (string) (bool) Jetpack::is_multi_network();
1249
	}
1250
1251
	/**
1252
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1253
	 *
1254
	 * @param  string  $option
1255
	 * @return boolean
1256
	 */
1257
	public function is_multisite( $option ) {
1258
		return (string) (bool) is_multisite();
1259
	}
1260
1261
	/**
1262
	 * Implemented since there is no core is multi network function
1263
	 * Right now there is no way to tell if we which network is the dominant network on the system
1264
	 *
1265
	 * @since  3.3
1266
	 * @return boolean
1267
	 */
1268
	public static function is_multi_network() {
1269
		global  $wpdb;
1270
1271
		// if we don't have a multi site setup no need to do any more
1272
		if ( ! is_multisite() ) {
1273
			return false;
1274
		}
1275
1276
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1277
		if ( $num_sites > 1 ) {
1278
			return true;
1279
		} else {
1280
			return false;
1281
		}
1282
	}
1283
1284
	/**
1285
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1286
	 * @return null
1287
	 */
1288
	function update_jetpack_main_network_site_option() {
1289
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1290
	}
1291
	/**
1292
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1293
	 *
1294
	 */
1295
	function update_jetpack_network_settings() {
1296
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1297
		// Only sync this info for the main network site.
1298
	}
1299
1300
	/**
1301
	 * Get back if the current site is single user site.
1302
	 *
1303
	 * @return bool
1304
	 */
1305
	public static function is_single_user_site() {
1306
		global $wpdb;
1307
1308 View Code Duplication
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1309
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1310
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1311
		}
1312
		return 1 === (int) $some_users;
1313
	}
1314
1315
	/**
1316
	 * Returns true if the site has file write access false otherwise.
1317
	 * @return string ( '1' | '0' )
1318
	 **/
1319
	public static function file_system_write_access() {
1320
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1321
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
1322
		}
1323
1324
		require_once( ABSPATH . 'wp-admin/includes/template.php' );
1325
1326
		$filesystem_method = get_filesystem_method();
1327
		if ( $filesystem_method === 'direct' ) {
1328
			return 1;
1329
		}
1330
1331
		ob_start();
1332
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1333
		ob_end_clean();
1334
		if ( $filesystem_credentials_are_stored ) {
1335
			return 1;
1336
		}
1337
		return 0;
1338
	}
1339
1340
	/**
1341
	 * Finds out if a site is using a version control system.
1342
	 * @return string ( '1' | '0' )
1343
	 **/
1344
	public static function is_version_controlled() {
1345
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Jetpack_Sync_Functions::is_version_controlled' );
1346
		return (string) (int) Jetpack_Sync_Functions::is_version_controlled();
1347
	}
1348
1349
	/**
1350
	 * Determines whether the current theme supports featured images or not.
1351
	 * @return string ( '1' | '0' )
1352
	 */
1353
	public static function featured_images_enabled() {
1354
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1355
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1356
	}
1357
1358
	/**
1359
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1360
	 *
1361
	 * @deprecated 4.7 use get_avatar_url instead.
1362
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1363
	 * @param int $size Size of the avatar image
1364
	 * @param string $default URL to a default image to use if no avatar is available
1365
	 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
1366
	 *
1367
	 * @return array
1368
	 */
1369
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1370
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1371
		return get_avatar_url( $id_or_email, array(
1372
			'size' => $size,
1373
			'default' => $default,
1374
			'force_default' => $force_display,
1375
		) );
1376
	}
1377
1378
	/**
1379
	 * jetpack_updates is saved in the following schema:
1380
	 *
1381
	 * array (
1382
	 *      'plugins'                       => (int) Number of plugin updates available.
1383
	 *      'themes'                        => (int) Number of theme updates available.
1384
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1385
	 *      'translations'                  => (int) Number of translation updates available.
1386
	 *      'total'                         => (int) Total of all available updates.
1387
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1388
	 * )
1389
	 * @return array
1390
	 */
1391
	public static function get_updates() {
1392
		$update_data = wp_get_update_data();
1393
1394
		// Stores the individual update counts as well as the total count.
1395
		if ( isset( $update_data['counts'] ) ) {
1396
			$updates = $update_data['counts'];
1397
		}
1398
1399
		// If we need to update WordPress core, let's find the latest version number.
1400 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1401
			$cur = get_preferred_from_update_core();
1402
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1403
				$updates['wp_update_version'] = $cur->current;
1404
			}
1405
		}
1406
		return isset( $updates ) ? $updates : array();
1407
	}
1408
1409
	public static function get_update_details() {
1410
		$update_details = array(
1411
			'update_core' => get_site_transient( 'update_core' ),
1412
			'update_plugins' => get_site_transient( 'update_plugins' ),
1413
			'update_themes' => get_site_transient( 'update_themes' ),
1414
		);
1415
		return $update_details;
1416
	}
1417
1418
	public static function refresh_update_data() {
1419
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1420
1421
	}
1422
1423
	public static function refresh_theme_data() {
1424
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1425
	}
1426
1427
	/**
1428
	 * Is Jetpack active?
1429
	 */
1430
	public static function is_active() {
1431
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1432
	}
1433
1434
	/**
1435
	 * Make an API call to WordPress.com for plan status
1436
	 *
1437
	 * @uses Jetpack_Options::get_option()
1438
	 * @uses Jetpack_Client::wpcom_json_api_request_as_blog()
1439
	 * @uses update_option()
1440
	 *
1441
	 * @access public
1442
	 * @static
1443
	 *
1444
	 * @return bool True if plan is updated, false if no update
1445
	 */
1446
	public static function refresh_active_plan_from_wpcom() {
1447
		// Make the API request
1448
		$request = sprintf( '/sites/%d', Jetpack_Options::get_option( 'id' ) );
1449
		$response = Jetpack_Client::wpcom_json_api_request_as_blog( $request, '1.1' );
1450
1451
		// Bail if there was an error or malformed response
1452
		if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
1453
			return false;
1454
		}
1455
1456
		// Decode the results
1457
		$results = json_decode( $response['body'], true );
1458
1459
		// Bail if there were no results or plan details returned
1460
		if ( ! is_array( $results ) || ! isset( $results['plan'] ) ) {
1461
			return false;
1462
		}
1463
1464
		// Store the option and return true if updated
1465
		return update_option( 'jetpack_active_plan', $results['plan'] );
1466
	}
1467
1468
	/**
1469
	 * Get the plan that this Jetpack site is currently using
1470
	 *
1471
	 * @uses get_option()
1472
	 *
1473
	 * @access public
1474
	 * @static
1475
	 *
1476
	 * @return array Active Jetpack plan details
1477
	 */
1478
	public static function get_active_plan() {
1479
		global $active_plan_cache;
1480
1481
		// this can be expensive to compute so we cache for the duration of a request
1482
		if ( is_array( $active_plan_cache ) && ! empty( $active_plan_cache ) ) {
1483
			return $active_plan_cache;
1484
		}
1485
1486
		$plan = get_option( 'jetpack_active_plan', array() );
1487
1488
		// Set the default options
1489
		$plan = wp_parse_args( $plan, array(
1490
			'product_slug' => 'jetpack_free',
1491
			'class'        => 'free',
1492
			'features'     => array(
1493
				'active' => array()
1494
			),
1495
		) );
1496
1497
		$supports = array();
1498
1499
		// Define what paid modules are supported by personal plans
1500
		$personal_plans = array(
1501
			'jetpack_personal',
1502
			'jetpack_personal_monthly',
1503
			'personal-bundle',
1504
			'personal-bundle-2y',
1505
		);
1506
1507
		if ( in_array( $plan['product_slug'], $personal_plans ) ) {
1508
			// special support value, not a module but a separate plugin
1509
			$supports[] = 'akismet';
1510
			$plan['class'] = 'personal';
1511
		}
1512
1513
		// Define what paid modules are supported by premium plans
1514
		$premium_plans = array(
1515
			'jetpack_premium',
1516
			'jetpack_premium_monthly',
1517
			'value_bundle',
1518
			'value_bundle-2y',
1519
		);
1520
1521 View Code Duplication
		if ( in_array( $plan['product_slug'], $premium_plans ) ) {
1522
			$supports[] = 'akismet';
1523
			$supports[] = 'simple-payments';
1524
			$supports[] = 'vaultpress';
1525
			$supports[] = 'videopress';
1526
			$plan['class'] = 'premium';
1527
		}
1528
1529
		// Define what paid modules are supported by professional plans
1530
		$business_plans = array(
1531
			'jetpack_business',
1532
			'jetpack_business_monthly',
1533
			'business-bundle',
1534
			'business-bundle-2y',
1535
			'vip',
1536
		);
1537
1538 View Code Duplication
		if ( in_array( $plan['product_slug'], $business_plans ) ) {
1539
			$supports[] = 'akismet';
1540
			$supports[] = 'simple-payments';
1541
			$supports[] = 'vaultpress';
1542
			$supports[] = 'videopress';
1543
			$plan['class'] = 'business';
1544
		}
1545
1546
		// get available features
1547
		foreach ( self::get_available_modules() as $module_slug ) {
1548
			$module = self::get_module( $module_slug );
1549
			if ( ! isset( $module ) || ! is_array( $module ) ) {
1550
				continue;
1551
			}
1552
			if ( in_array( 'free', $module['plan_classes'] ) || in_array( $plan['class'], $module['plan_classes'] ) ) {
1553
				$supports[] = $module_slug;
1554
			}
1555
		}
1556
1557
		$plan['supports'] = $supports;
1558
1559
		$active_plan_cache = $plan;
1560
1561
		return $plan;
1562
	}
1563
1564
	/**
1565
	 * Determine whether the active plan supports a particular feature
1566
	 *
1567
	 * @uses Jetpack::get_active_plan()
1568
	 *
1569
	 * @access public
1570
	 * @static
1571
	 *
1572
	 * @return bool True if plan supports feature, false if not
1573
	 */
1574
	public static function active_plan_supports( $feature ) {
1575
		$plan = Jetpack::get_active_plan();
1576
1577
		// Manually mapping WordPress.com features to Jetpack module slugs
1578
		foreach ( $plan['features']['active'] as $wpcom_feature ) {
1579
			switch ( $wpcom_feature ) {
1580
				case 'wordads-jetpack';
1581
1582
				// WordAds are supported for this site
1583
				if ( 'wordads' === $feature ) {
1584
					return true;
1585
				}
1586
				break;
1587
			}
1588
		}
1589
1590
		if (
1591
			in_array( $feature, $plan['supports'] )
1592
			|| in_array( $feature, $plan['features']['active'] )
1593
		) {
1594
			return true;
1595
		}
1596
1597
		return false;
1598
	}
1599
1600
	/**
1601
	 * Is Jetpack in development (offline) mode?
1602
	 */
1603
	public static function is_development_mode() {
1604
		$development_mode = false;
1605
1606
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1607
			$development_mode = JETPACK_DEV_DEBUG;
1608
		} elseif ( $site_url = site_url() ) {
1609
			$development_mode = false === strpos( $site_url, '.' );
1610
		}
1611
1612
		/**
1613
		 * Filters Jetpack's development mode.
1614
		 *
1615
		 * @see https://jetpack.com/support/development-mode/
1616
		 *
1617
		 * @since 2.2.1
1618
		 *
1619
		 * @param bool $development_mode Is Jetpack's development mode active.
1620
		 */
1621
		$development_mode = ( bool ) apply_filters( 'jetpack_development_mode', $development_mode );
1622
		return $development_mode;
1623
	}
1624
1625
	/**
1626
	 * Whether the site is currently onboarding or not.
1627
	 * A site is considered as being onboarded if it currently has an onboarding token.
1628
	 *
1629
	 * @since 5.8
1630
	 *
1631
	 * @access public
1632
	 * @static
1633
	 *
1634
	 * @return bool True if the site is currently onboarding, false otherwise
1635
	 */
1636
	public static function is_onboarding() {
1637
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1638
	}
1639
1640
	/**
1641
	* Get Jetpack development mode notice text and notice class.
1642
	*
1643
	* Mirrors the checks made in Jetpack::is_development_mode
1644
	*
1645
	*/
1646
	public static function show_development_mode_notice() {
1647
		if ( Jetpack::is_development_mode() ) {
1648
			if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1649
				$notice = sprintf(
1650
					/* translators: %s is a URL */
1651
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the JETPACK_DEV_DEBUG constant being defined in wp-config.php or elsewhere.', 'jetpack' ),
1652
					'https://jetpack.com/support/development-mode/'
1653
				);
1654
			} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1655
				$notice = sprintf(
1656
					/* translators: %s is a URL */
1657
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via site URL lacking a dot (e.g. http://localhost).', 'jetpack' ),
1658
					'https://jetpack.com/support/development-mode/'
1659
				);
1660
			} else {
1661
				$notice = sprintf(
1662
					/* translators: %s is a URL */
1663
					__( 'In <a href="%s" target="_blank">Development Mode</a>, via the jetpack_development_mode filter.', 'jetpack' ),
1664
					'https://jetpack.com/support/development-mode/'
1665
				);
1666
			}
1667
1668
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1669
		}
1670
1671
		// Throw up a notice if using a development version and as for feedback.
1672
		if ( Jetpack::is_development_version() ) {
1673
			/* translators: %s is a URL */
1674
			$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/' );
1675
1676
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1677
		}
1678
		// Throw up a notice if using staging mode
1679
		if ( Jetpack::is_staging_site() ) {
1680
			/* translators: %s is a URL */
1681
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.com/support/staging-sites/' );
1682
1683
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1684
		}
1685
	}
1686
1687
	/**
1688
	 * Whether Jetpack's version maps to a public release, or a development version.
1689
	 */
1690
	public static function is_development_version() {
1691
		/**
1692
		 * Allows filtering whether this is a development version of Jetpack.
1693
		 *
1694
		 * This filter is especially useful for tests.
1695
		 *
1696
		 * @since 4.3.0
1697
		 *
1698
		 * @param bool $development_version Is this a develoment version of Jetpack?
1699
		 */
1700
		return (bool) apply_filters(
1701
			'jetpack_development_version',
1702
			! preg_match( '/^\d+(\.\d+)+$/', Jetpack_Constants::get_constant( 'JETPACK__VERSION' ) )
1703
		);
1704
	}
1705
1706
	/**
1707
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1708
	 */
1709
	public static function is_user_connected( $user_id = false ) {
1710
		$user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
1711
		if ( ! $user_id ) {
1712
			return false;
1713
		}
1714
1715
		return (bool) Jetpack_Data::get_access_token( $user_id );
1716
	}
1717
1718
	/**
1719
	 * Get the wpcom user data of the current|specified connected user.
1720
	 */
1721
	public static function get_connected_user_data( $user_id = null ) {
1722
		if ( ! $user_id ) {
1723
			$user_id = get_current_user_id();
1724
		}
1725
1726
		$transient_key = "jetpack_connected_user_data_$user_id";
1727
1728
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1729
			return $cached_user_data;
1730
		}
1731
1732
		Jetpack::load_xml_rpc_client();
1733
		$xml = new Jetpack_IXR_Client( array(
1734
			'user_id' => $user_id,
1735
		) );
1736
		$xml->query( 'wpcom.getUser' );
1737
		if ( ! $xml->isError() ) {
1738
			$user_data = $xml->getResponse();
1739
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1740
			return $user_data;
1741
		}
1742
1743
		return false;
1744
	}
1745
1746
	/**
1747
	 * Get the wpcom email of the current|specified connected user.
1748
	 */
1749 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1750
		if ( ! $user_id ) {
1751
			$user_id = get_current_user_id();
1752
		}
1753
		Jetpack::load_xml_rpc_client();
1754
		$xml = new Jetpack_IXR_Client( array(
1755
			'user_id' => $user_id,
1756
		) );
1757
		$xml->query( 'wpcom.getUserEmail' );
1758
		if ( ! $xml->isError() ) {
1759
			return $xml->getResponse();
1760
		}
1761
		return false;
1762
	}
1763
1764
	/**
1765
	 * Get the wpcom email of the master user.
1766
	 */
1767
	public static function get_master_user_email() {
1768
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1769
		if ( $master_user_id ) {
1770
			return self::get_connected_user_email( $master_user_id );
1771
		}
1772
		return '';
1773
	}
1774
1775
	function current_user_is_connection_owner() {
1776
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
1777
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1778
	}
1779
1780
	/**
1781
	 * Gets current user IP address.
1782
	 *
1783
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1784
	 *
1785
	 * @return string                  Current user IP address.
1786
	 */
1787
	public static function current_user_ip( $check_all_headers = false ) {
1788
		if ( $check_all_headers ) {
1789
			foreach ( array(
1790
				'HTTP_CF_CONNECTING_IP',
1791
				'HTTP_CLIENT_IP',
1792
				'HTTP_X_FORWARDED_FOR',
1793
				'HTTP_X_FORWARDED',
1794
				'HTTP_X_CLUSTER_CLIENT_IP',
1795
				'HTTP_FORWARDED_FOR',
1796
				'HTTP_FORWARDED',
1797
				'HTTP_VIA',
1798
			) as $key ) {
1799
				if ( ! empty( $_SERVER[ $key ] ) ) {
1800
					return $_SERVER[ $key ];
1801
				}
1802
			}
1803
		}
1804
1805
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1806
	}
1807
1808
	/**
1809
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1810
	 */
1811
	function extra_oembed_providers() {
1812
		// Cloudup: https://dev.cloudup.com/#oembed
1813
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1814
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1815
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1816
		wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true );
1817
		wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true );
1818
		wp_oembed_add_provider( '#https?://(www\.)?icloud\.com/keynote/.*#i', 'https://iwmb.icloud.com/iwmb/oembed', true );
1819
	}
1820
1821
	/**
1822
	 * Synchronize connected user role changes
1823
	 */
1824
	function user_role_change( $user_id ) {
1825
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Jetpack_Sync_Users::user_role_change()' );
1826
		Jetpack_Sync_Users::user_role_change( $user_id );
1827
	}
1828
1829
	/**
1830
	 * Loads the currently active modules.
1831
	 */
1832
	public static function load_modules() {
1833
		if (
1834
			! self::is_active()
1835
			&& ! self::is_development_mode()
1836
			&& ! self::is_onboarding()
1837
			&& (
1838
				! is_multisite()
1839
				|| ! get_site_option( 'jetpack_protect_active' )
1840
			)
1841
		) {
1842
			return;
1843
		}
1844
1845
		$version = Jetpack_Options::get_option( 'version' );
1846 View Code Duplication
		if ( ! $version ) {
1847
			$version = $old_version = JETPACK__VERSION . ':' . time();
1848
			/** This action is documented in class.jetpack.php */
1849
			do_action( 'updating_jetpack_version', $version, false );
1850
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1851
		}
1852
		list( $version ) = explode( ':', $version );
1853
1854
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1855
1856
		$modules_data = array();
1857
1858
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1859
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1860
			$updated_modules = array();
1861
			foreach ( $modules as $module ) {
1862
				$modules_data[ $module ] = Jetpack::get_module( $module );
1863
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1864
					continue;
1865
				}
1866
1867
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1868
					continue;
1869
				}
1870
1871
				$updated_modules[] = $module;
1872
			}
1873
1874
			$modules = array_diff( $modules, $updated_modules );
1875
		}
1876
1877
		$is_development_mode = Jetpack::is_development_mode();
1878
1879
		foreach ( $modules as $index => $module ) {
1880
			// If we're in dev mode, disable modules requiring a connection
1881
			if ( $is_development_mode ) {
1882
				// Prime the pump if we need to
1883
				if ( empty( $modules_data[ $module ] ) ) {
1884
					$modules_data[ $module ] = Jetpack::get_module( $module );
1885
				}
1886
				// If the module requires a connection, but we're in local mode, don't include it.
1887
				if ( $modules_data[ $module ]['requires_connection'] ) {
1888
					continue;
1889
				}
1890
			}
1891
1892
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1893
				continue;
1894
			}
1895
1896
			if ( ! include_once( Jetpack::get_module_path( $module ) ) ) {
1897
				unset( $modules[ $index ] );
1898
				self::update_active_modules( array_values( $modules ) );
1899
				continue;
1900
			}
1901
1902
			/**
1903
			 * Fires when a specific module is loaded.
1904
			 * The dynamic part of the hook, $module, is the module slug.
1905
			 *
1906
			 * @since 1.1.0
1907
			 */
1908
			do_action( 'jetpack_module_loaded_' . $module );
1909
		}
1910
1911
		/**
1912
		 * Fires when all the modules are loaded.
1913
		 *
1914
		 * @since 1.1.0
1915
		 */
1916
		do_action( 'jetpack_modules_loaded' );
1917
1918
		// 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.
1919
		require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
1920
	}
1921
1922
	/**
1923
	 * Check if Jetpack's REST API compat file should be included
1924
	 * @action plugins_loaded
1925
	 * @return null
1926
	 */
1927
	public function check_rest_api_compat() {
1928
		/**
1929
		 * Filters the list of REST API compat files to be included.
1930
		 *
1931
		 * @since 2.2.5
1932
		 *
1933
		 * @param array $args Array of REST API compat files to include.
1934
		 */
1935
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1936
1937
		if ( function_exists( 'bbpress' ) )
1938
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
1939
1940
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
1941
			require_once $_jetpack_rest_api_compat_include;
1942
	}
1943
1944
	/**
1945
	 * Gets all plugins currently active in values, regardless of whether they're
1946
	 * traditionally activated or network activated.
1947
	 *
1948
	 * @todo Store the result in core's object cache maybe?
1949
	 */
1950
	public static function get_active_plugins() {
1951
		$active_plugins = (array) get_option( 'active_plugins', array() );
1952
1953
		if ( is_multisite() ) {
1954
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1955
			// whereas active_plugins stores them in the values.
1956
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1957
			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...
1958
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1959
			}
1960
		}
1961
1962
		sort( $active_plugins );
1963
1964
		return array_unique( $active_plugins );
1965
	}
1966
1967
	/**
1968
	 * Gets and parses additional plugin data to send with the heartbeat data
1969
	 *
1970
	 * @since 3.8.1
1971
	 *
1972
	 * @return array Array of plugin data
1973
	 */
1974
	public static function get_parsed_plugin_data() {
1975
		if ( ! function_exists( 'get_plugins' ) ) {
1976
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1977
		}
1978
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
1979
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
1980
		$active_plugins = Jetpack::get_active_plugins();
1981
1982
		$plugins = array();
1983
		foreach ( $all_plugins as $path => $plugin_data ) {
1984
			$plugins[ $path ] = array(
1985
					'is_active' => in_array( $path, $active_plugins ),
1986
					'file'      => $path,
1987
					'name'      => $plugin_data['Name'],
1988
					'version'   => $plugin_data['Version'],
1989
					'author'    => $plugin_data['Author'],
1990
			);
1991
		}
1992
1993
		return $plugins;
1994
	}
1995
1996
	/**
1997
	 * Gets and parses theme data to send with the heartbeat data
1998
	 *
1999
	 * @since 3.8.1
2000
	 *
2001
	 * @return array Array of theme data
2002
	 */
2003
	public static function get_parsed_theme_data() {
2004
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
2005
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
2006
2007
		$themes = array();
2008
		foreach ( $all_themes as $slug => $theme_data ) {
2009
			$theme_headers = array();
2010
			foreach ( $header_keys as $header_key ) {
2011
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2012
			}
2013
2014
			$themes[ $slug ] = array(
2015
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
2016
					'slug' => $slug,
2017
					'theme_root' => $theme_data->get_theme_root_uri(),
2018
					'parent' => $theme_data->parent(),
2019
					'headers' => $theme_headers
2020
			);
2021
		}
2022
2023
		return $themes;
2024
	}
2025
2026
	/**
2027
	 * Checks whether a specific plugin is active.
2028
	 *
2029
	 * We don't want to store these in a static variable, in case
2030
	 * there are switch_to_blog() calls involved.
2031
	 */
2032
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2033
		return in_array( $plugin, self::get_active_plugins() );
2034
	}
2035
2036
	/**
2037
	 * Check if Jetpack's Open Graph tags should be used.
2038
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2039
	 *
2040
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2041
	 * @action plugins_loaded
2042
	 * @return null
2043
	 */
2044
	public function check_open_graph() {
2045
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
2046
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2047
		}
2048
2049
		$active_plugins = self::get_active_plugins();
2050
2051
		if ( ! empty( $active_plugins ) ) {
2052
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2053
				if ( in_array( $plugin, $active_plugins ) ) {
2054
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2055
					break;
2056
				}
2057
			}
2058
		}
2059
2060
		/**
2061
		 * Allow the addition of Open Graph Meta Tags to all pages.
2062
		 *
2063
		 * @since 2.0.3
2064
		 *
2065
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2066
		 */
2067
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2068
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2069
		}
2070
	}
2071
2072
	/**
2073
	 * Check if Jetpack's Twitter tags should be used.
2074
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2075
	 *
2076
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2077
	 * @action plugins_loaded
2078
	 * @return null
2079
	 */
2080
	public function check_twitter_tags() {
2081
2082
		$active_plugins = self::get_active_plugins();
2083
2084
		if ( ! empty( $active_plugins ) ) {
2085
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2086
				if ( in_array( $plugin, $active_plugins ) ) {
2087
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2088
					break;
2089
				}
2090
			}
2091
		}
2092
2093
		/**
2094
		 * Allow Twitter Card Meta tags to be disabled.
2095
		 *
2096
		 * @since 2.6.0
2097
		 *
2098
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2099
		 */
2100
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2101
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2102
		}
2103
	}
2104
2105
	/**
2106
	 * Allows plugins to submit security reports.
2107
 	 *
2108
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
2109
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
2110
	 * @param array   $args         See definitions above
2111
	 */
2112
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2113
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2114
	}
2115
2116
/* Jetpack Options API */
2117
2118
	public static function get_option_names( $type = 'compact' ) {
2119
		return Jetpack_Options::get_option_names( $type );
2120
	}
2121
2122
	/**
2123
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2124
 	 *
2125
	 * @param string $name    Option name
2126
	 * @param mixed  $default (optional)
2127
	 */
2128
	public static function get_option( $name, $default = false ) {
2129
		return Jetpack_Options::get_option( $name, $default );
2130
	}
2131
2132
	/**
2133
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2134
 	 *
2135
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2136
	 * @param string $name  Option name
2137
	 * @param mixed  $value Option value
2138
	 */
2139
	public static function update_option( $name, $value ) {
2140
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2141
		return Jetpack_Options::update_option( $name, $value );
2142
	}
2143
2144
	/**
2145
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2146
 	 *
2147
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2148
	 * @param array $array array( option name => option value, ... )
2149
	 */
2150
	public static function update_options( $array ) {
2151
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2152
		return Jetpack_Options::update_options( $array );
2153
	}
2154
2155
	/**
2156
	 * Deletes the given option.  May be passed multiple option names as an array.
2157
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2158
	 *
2159
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2160
	 * @param string|array $names
2161
	 */
2162
	public static function delete_option( $names ) {
2163
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2164
		return Jetpack_Options::delete_option( $names );
2165
	}
2166
2167
	/**
2168
	 * Enters a user token into the user_tokens option
2169
	 *
2170
	 * @param int $user_id
2171
	 * @param string $token
2172
	 * return bool
2173
	 */
2174
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2175
		// not designed for concurrent updates
2176
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
2177
		if ( ! is_array( $user_tokens ) )
2178
			$user_tokens = array();
2179
		$user_tokens[$user_id] = $token;
2180
		if ( $is_master_user ) {
2181
			$master_user = $user_id;
2182
			$options     = compact( 'user_tokens', 'master_user' );
2183
		} else {
2184
			$options = compact( 'user_tokens' );
2185
		}
2186
		return Jetpack_Options::update_options( $options );
2187
	}
2188
2189
	/**
2190
	 * Returns an array of all PHP files in the specified absolute path.
2191
	 * Equivalent to glob( "$absolute_path/*.php" ).
2192
	 *
2193
	 * @param string $absolute_path The absolute path of the directory to search.
2194
	 * @return array Array of absolute paths to the PHP files.
2195
	 */
2196
	public static function glob_php( $absolute_path ) {
2197
		if ( function_exists( 'glob' ) ) {
2198
			return glob( "$absolute_path/*.php" );
2199
		}
2200
2201
		$absolute_path = untrailingslashit( $absolute_path );
2202
		$files = array();
2203
		if ( ! $dir = @opendir( $absolute_path ) ) {
2204
			return $files;
2205
		}
2206
2207
		while ( false !== $file = readdir( $dir ) ) {
2208
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2209
				continue;
2210
			}
2211
2212
			$file = "$absolute_path/$file";
2213
2214
			if ( ! is_file( $file ) ) {
2215
				continue;
2216
			}
2217
2218
			$files[] = $file;
2219
		}
2220
2221
		closedir( $dir );
2222
2223
		return $files;
2224
	}
2225
2226
	public static function activate_new_modules( $redirect = false ) {
2227
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
2228
			return;
2229
		}
2230
2231
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2232 View Code Duplication
		if ( ! $jetpack_old_version ) {
2233
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2234
			/** This action is documented in class.jetpack.php */
2235
			do_action( 'updating_jetpack_version', $version, false );
2236
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2237
		}
2238
2239
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2240
2241
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2242
			return;
2243
		}
2244
2245
		$active_modules     = Jetpack::get_active_modules();
2246
		$reactivate_modules = array();
2247
		foreach ( $active_modules as $active_module ) {
2248
			$module = Jetpack::get_module( $active_module );
2249
			if ( ! isset( $module['changed'] ) ) {
2250
				continue;
2251
			}
2252
2253
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2254
				continue;
2255
			}
2256
2257
			$reactivate_modules[] = $active_module;
2258
			Jetpack::deactivate_module( $active_module );
2259
		}
2260
2261
		$new_version = JETPACK__VERSION . ':' . time();
2262
		/** This action is documented in class.jetpack.php */
2263
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2264
		Jetpack_Options::update_options(
2265
			array(
2266
				'version'     => $new_version,
2267
				'old_version' => $jetpack_old_version,
2268
			)
2269
		);
2270
2271
		Jetpack::state( 'message', 'modules_activated' );
2272
		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...
2273
2274
		if ( $redirect ) {
2275
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2276
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2277
				$page = $_GET['page'];
2278
			}
2279
2280
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
2281
			exit;
2282
		}
2283
	}
2284
2285
	/**
2286
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2287
	 * Make sure to tuck away module "library" files in a sub-directory.
2288
	 */
2289
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2290
		static $modules = null;
2291
2292
		if ( ! isset( $modules ) ) {
2293
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2294
			// Use the cache if we're on the front-end and it's available...
2295
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2296
				$modules = $available_modules_option[ JETPACK__VERSION ];
2297
			} else {
2298
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2299
2300
				$modules = array();
2301
2302
				foreach ( $files as $file ) {
2303
					if ( ! $headers = Jetpack::get_module( $file ) ) {
2304
						continue;
2305
					}
2306
2307
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
2308
				}
2309
2310
				Jetpack_Options::update_option( 'available_modules', array(
2311
					JETPACK__VERSION => $modules,
2312
				) );
2313
			}
2314
		}
2315
2316
		/**
2317
		 * Filters the array of modules available to be activated.
2318
		 *
2319
		 * @since 2.4.0
2320
		 *
2321
		 * @param array $modules Array of available modules.
2322
		 * @param string $min_version Minimum version number required to use modules.
2323
		 * @param string $max_version Maximum version number required to use modules.
2324
		 */
2325
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
2326
2327
		if ( ! $min_version && ! $max_version ) {
2328
			return array_keys( $mods );
2329
		}
2330
2331
		$r = array();
2332
		foreach ( $mods as $slug => $introduced ) {
2333
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2334
				continue;
2335
			}
2336
2337
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2338
				continue;
2339
			}
2340
2341
			$r[] = $slug;
2342
		}
2343
2344
		return $r;
2345
	}
2346
2347
	/**
2348
	 * Default modules loaded on activation.
2349
	 */
2350
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2351
		$return = array();
2352
2353
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
2354
			$module_data = Jetpack::get_module( $module );
2355
2356
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2357
				case 'yes' :
2358
					$return[] = $module;
2359
					break;
2360
				case 'public' :
2361
					if ( Jetpack_Options::get_option( 'public' ) ) {
2362
						$return[] = $module;
2363
					}
2364
					break;
2365
				case 'no' :
2366
				default :
2367
					break;
2368
			}
2369
		}
2370
		/**
2371
		 * Filters the array of default modules.
2372
		 *
2373
		 * @since 2.5.0
2374
		 *
2375
		 * @param array $return Array of default modules.
2376
		 * @param string $min_version Minimum version number required to use modules.
2377
		 * @param string $max_version Maximum version number required to use modules.
2378
		 */
2379
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
2380
	}
2381
2382
	/**
2383
	 * Checks activated modules during auto-activation to determine
2384
	 * if any of those modules are being deprecated.  If so, close
2385
	 * them out, and add any replacement modules.
2386
	 *
2387
	 * Runs at priority 99 by default.
2388
	 *
2389
	 * This is run late, so that it can still activate a module if
2390
	 * the new module is a replacement for another that the user
2391
	 * currently has active, even if something at the normal priority
2392
	 * would kibosh everything.
2393
	 *
2394
	 * @since 2.6
2395
	 * @uses jetpack_get_default_modules filter
2396
	 * @param array $modules
2397
	 * @return array
2398
	 */
2399
	function handle_deprecated_modules( $modules ) {
2400
		$deprecated_modules = array(
2401
			'debug'            => null,  // Closed out and moved to ./class.jetpack-debugger.php
2402
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2403
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2404
		);
2405
2406
		// Don't activate SSO if they never completed activating WPCC.
2407
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
2408
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2409
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2410
				$deprecated_modules['wpcc'] = null;
2411
			}
2412
		}
2413
2414
		foreach ( $deprecated_modules as $module => $replacement ) {
2415
			if ( Jetpack::is_module_active( $module ) ) {
2416
				self::deactivate_module( $module );
2417
				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...
2418
					$modules[] = $replacement;
2419
				}
2420
			}
2421
		}
2422
2423
		return array_unique( $modules );
2424
	}
2425
2426
	/**
2427
	 * Checks activated plugins during auto-activation to determine
2428
	 * if any of those plugins are in the list with a corresponding module
2429
	 * that is not compatible with the plugin. The module will not be allowed
2430
	 * to auto-activate.
2431
	 *
2432
	 * @since 2.6
2433
	 * @uses jetpack_get_default_modules filter
2434
	 * @param array $modules
2435
	 * @return array
2436
	 */
2437
	function filter_default_modules( $modules ) {
2438
2439
		$active_plugins = self::get_active_plugins();
2440
2441
		if ( ! empty( $active_plugins ) ) {
2442
2443
			// For each module we'd like to auto-activate...
2444
			foreach ( $modules as $key => $module ) {
2445
				// If there are potential conflicts for it...
2446
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2447
					// For each potential conflict...
2448
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2449
						// If that conflicting plugin is active...
2450
						if ( in_array( $plugin, $active_plugins ) ) {
2451
							// Remove that item from being auto-activated.
2452
							unset( $modules[ $key ] );
2453
						}
2454
					}
2455
				}
2456
			}
2457
		}
2458
2459
		return $modules;
2460
	}
2461
2462
	/**
2463
	 * Extract a module's slug from its full path.
2464
	 */
2465
	public static function get_module_slug( $file ) {
2466
		return str_replace( '.php', '', basename( $file ) );
2467
	}
2468
2469
	/**
2470
	 * Generate a module's path from its slug.
2471
	 */
2472
	public static function get_module_path( $slug ) {
2473
		return JETPACK__PLUGIN_DIR . "modules/$slug.php";
2474
	}
2475
2476
	/**
2477
	 * Load module data from module file. Headers differ from WordPress
2478
	 * plugin headers to avoid them being identified as standalone
2479
	 * plugins on the WordPress plugins page.
2480
	 */
2481
	public static function get_module( $module ) {
2482
		$headers = array(
2483
			'name'                      => 'Module Name',
2484
			'description'               => 'Module Description',
2485
			'jumpstart_desc'            => 'Jumpstart Description',
2486
			'sort'                      => 'Sort Order',
2487
			'recommendation_order'      => 'Recommendation Order',
2488
			'introduced'                => 'First Introduced',
2489
			'changed'                   => 'Major Changes In',
2490
			'deactivate'                => 'Deactivate',
2491
			'free'                      => 'Free',
2492
			'requires_connection'       => 'Requires Connection',
2493
			'auto_activate'             => 'Auto Activate',
2494
			'module_tags'               => 'Module Tags',
2495
			'feature'                   => 'Feature',
2496
			'additional_search_queries' => 'Additional Search Queries',
2497
			'plan_classes'              => 'Plans',
2498
		);
2499
2500
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2501
2502
		$mod = Jetpack::get_file_data( $file, $headers );
2503
		if ( empty( $mod['name'] ) ) {
2504
			return false;
2505
		}
2506
2507
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2508
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2509
		$mod['deactivate']              = empty( $mod['deactivate'] );
2510
		$mod['free']                    = empty( $mod['free'] );
2511
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2512
2513
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2514
			$mod['auto_activate'] = 'No';
2515
		} else {
2516
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2517
		}
2518
2519
		if ( $mod['module_tags'] ) {
2520
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2521
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2522
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2523
		} else {
2524
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2525
		}
2526
2527 View Code Duplication
		if ( $mod['plan_classes'] ) {
2528
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2529
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2530
		} else {
2531
			$mod['plan_classes'] = array( 'free' );
2532
		}
2533
2534 View Code Duplication
		if ( $mod['feature'] ) {
2535
			$mod['feature'] = explode( ',', $mod['feature'] );
2536
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2537
		} else {
2538
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2539
		}
2540
2541
		/**
2542
		 * Filters the feature array on a module.
2543
		 *
2544
		 * This filter allows you to control where each module is filtered: Recommended,
2545
		 * Jumpstart, and the default "Other" listing.
2546
		 *
2547
		 * @since 3.5.0
2548
		 *
2549
		 * @param array   $mod['feature'] The areas to feature this module:
2550
		 *     'Jumpstart' adds to the "Jumpstart" option to activate many modules at once.
2551
		 *     'Recommended' shows on the main Jetpack admin screen.
2552
		 *     'Other' should be the default if no other value is in the array.
2553
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2554
		 * @param array   $mod All the currently assembled module data.
2555
		 */
2556
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2557
2558
		/**
2559
		 * Filter the returned data about a module.
2560
		 *
2561
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2562
		 * so please be careful.
2563
		 *
2564
		 * @since 3.6.0
2565
		 *
2566
		 * @param array   $mod    The details of the requested module.
2567
		 * @param string  $module The slug of the module, e.g. sharedaddy
2568
		 * @param string  $file   The path to the module source file.
2569
		 */
2570
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2571
	}
2572
2573
	/**
2574
	 * Like core's get_file_data implementation, but caches the result.
2575
	 */
2576
	public static function get_file_data( $file, $headers ) {
2577
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2578
		$file_name = basename( $file );
2579
2580
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2581
2582
		$file_data_option = get_transient( $cache_key );
2583
2584
		if ( false === $file_data_option ) {
2585
			$file_data_option = array();
2586
		}
2587
2588
		$key           = md5( $file_name . serialize( $headers ) );
2589
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2590
2591
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2592
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2593
			return $file_data_option[ $key ];
2594
		}
2595
2596
		$data = get_file_data( $file, $headers );
2597
2598
		$file_data_option[ $key ] = $data;
2599
2600
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2601
2602
		return $data;
2603
	}
2604
2605
2606
	/**
2607
	 * Return translated module tag.
2608
	 *
2609
	 * @param string $tag Tag as it appears in each module heading.
2610
	 *
2611
	 * @return mixed
2612
	 */
2613
	public static function translate_module_tag( $tag ) {
2614
		return jetpack_get_module_i18n_tag( $tag );
2615
	}
2616
2617
	/**
2618
	 * Get i18n strings as a JSON-encoded string
2619
	 *
2620
	 * @return string The locale as JSON
2621
	 */
2622
	public static function get_i18n_data_json() {
2623
		$i18n_json = JETPACK__PLUGIN_DIR . 'languages/json/jetpack-' . get_user_locale() . '.json';
2624
2625
		if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
2626
			$locale_data = @file_get_contents( $i18n_json );
2627
			if ( $locale_data ) {
2628
				return $locale_data;
2629
			}
2630
		}
2631
2632
		// Return valid empty Jed locale
2633
		return json_encode( array(
2634
			'' => array(
2635
				'domain' => 'jetpack',
2636
				'lang'   => is_admin() ? get_user_locale() : get_locale(),
2637
			),
2638
		) );
2639
	}
2640
2641
	/**
2642
	 * Add locale data setup to wp-i18n
2643
	 *
2644
	 * Any Jetpack script that depends on wp-i18n should use this method to set up the locale.
2645
	 *
2646
	 * The locale setup depends on an adding inline script. This is error-prone and could easily
2647
	 * result in multiple additions of the same script when exactly 0 or 1 is desireable.
2648
	 *
2649
	 * This method provides a safe way to request the setup multiple times but add the script at
2650
	 * most once.
2651
	 *
2652
	 * @since 6.7.0
2653
	 *
2654
	 * @return void
2655
	 */
2656
	public static function setup_wp_i18n_locale_data() {
2657
		static $script_added = false;
2658
		if ( ! $script_added ) {
2659
			$script_added = true;
2660
			wp_add_inline_script(
2661
				'wp-i18n',
2662
				'wp.i18n.setLocaleData( ' . Jetpack::get_i18n_data_json() . ', \'jetpack\' );'
2663
			);
2664
		}
2665
	}
2666
2667
	/**
2668
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2669
	 *
2670
	 * @since 3.9.2
2671
	 *
2672
	 * @param array $modules
2673
	 *
2674
	 * @return string|void
2675
	 */
2676
	public static function get_translated_modules( $modules ) {
2677
		foreach ( $modules as $index => $module ) {
2678
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2679
			if ( isset( $module['name'] ) ) {
2680
				$modules[ $index ]['name'] = $i18n_module['name'];
2681
			}
2682
			if ( isset( $module['description'] ) ) {
2683
				$modules[ $index ]['description'] = $i18n_module['description'];
2684
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2685
			}
2686
		}
2687
		return $modules;
2688
	}
2689
2690
	/**
2691
	 * Get a list of activated modules as an array of module slugs.
2692
	 */
2693
	public static function get_active_modules() {
2694
		$active = Jetpack_Options::get_option( 'active_modules' );
2695
2696
		if ( ! is_array( $active ) ) {
2697
			$active = array();
2698
		}
2699
2700
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2701
			$active[] = 'vaultpress';
2702
		} else {
2703
			$active = array_diff( $active, array( 'vaultpress' ) );
2704
		}
2705
2706
		//If protect is active on the main site of a multisite, it should be active on all sites.
2707
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2708
			$active[] = 'protect';
2709
		}
2710
2711
		/**
2712
		 * Allow filtering of the active modules.
2713
		 *
2714
		 * Gives theme and plugin developers the power to alter the modules that
2715
		 * are activated on the fly.
2716
		 *
2717
		 * @since 5.8.0
2718
		 *
2719
		 * @param array $active Array of active module slugs.
2720
		 */
2721
		$active = apply_filters( 'jetpack_active_modules', $active );
2722
2723
		return array_unique( $active );
2724
	}
2725
2726
	/**
2727
	 * Check whether or not a Jetpack module is active.
2728
	 *
2729
	 * @param string $module The slug of a Jetpack module.
2730
	 * @return bool
2731
	 *
2732
	 * @static
2733
	 */
2734
	public static function is_module_active( $module ) {
2735
		return in_array( $module, self::get_active_modules() );
2736
	}
2737
2738
	public static function is_module( $module ) {
2739
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2740
	}
2741
2742
	/**
2743
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2744
	 *
2745
	 * @param bool $catch True to start catching, False to stop.
2746
	 *
2747
	 * @static
2748
	 */
2749
	public static function catch_errors( $catch ) {
2750
		static $display_errors, $error_reporting;
2751
2752
		if ( $catch ) {
2753
			$display_errors  = @ini_set( 'display_errors', 1 );
2754
			$error_reporting = @error_reporting( E_ALL );
2755
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2756
		} else {
2757
			@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...
2758
			@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...
2759
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2760
		}
2761
	}
2762
2763
	/**
2764
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2765
	 */
2766
	public static function catch_errors_on_shutdown() {
2767
		Jetpack::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2768
	}
2769
2770
	/**
2771
	 * Rewrite any string to make paths easier to read.
2772
	 *
2773
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2774
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2775
	 *
2776
	 * @param $string
2777
	 * @return mixed
2778
	 */
2779
	public static function alias_directories( $string ) {
2780
		// ABSPATH has a trailing slash.
2781
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2782
		// WP_CONTENT_DIR does not have a trailing slash.
2783
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2784
2785
		return $string;
2786
	}
2787
2788
	public static function activate_default_modules(
2789
		$min_version = false,
2790
		$max_version = false,
2791
		$other_modules = array(),
2792
		$redirect = true,
2793
		$send_state_messages = true
2794
	) {
2795
		$jetpack = Jetpack::init();
2796
2797
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2798
		$modules = array_merge( $other_modules, $modules );
2799
2800
		// Look for standalone plugins and disable if active.
2801
2802
		$to_deactivate = array();
2803
		foreach ( $modules as $module ) {
2804
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2805
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2806
			}
2807
		}
2808
2809
		$deactivated = array();
2810
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2811
			list( $probable_file, $probable_title ) = $deactivate_me;
2812
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2813
				$deactivated[] = $module;
2814
			}
2815
		}
2816
2817
		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...
2818
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2819
2820
			$url = add_query_arg(
2821
				array(
2822
					'action'   => 'activate_default_modules',
2823
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2824
				),
2825
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2826
			);
2827
			wp_safe_redirect( $url );
2828
			exit;
2829
		}
2830
2831
		/**
2832
		 * Fires before default modules are activated.
2833
		 *
2834
		 * @since 1.9.0
2835
		 *
2836
		 * @param string $min_version Minimum version number required to use modules.
2837
		 * @param string $max_version Maximum version number required to use modules.
2838
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2839
		 */
2840
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2841
2842
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2843
		if ( $send_state_messages ) {
2844
			Jetpack::restate();
2845
			Jetpack::catch_errors( true );
2846
		}
2847
2848
		$active = Jetpack::get_active_modules();
2849
2850
		foreach ( $modules as $module ) {
2851
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2852
				$active[] = $module;
2853
				self::update_active_modules( $active );
2854
				continue;
2855
			}
2856
2857
			if ( $send_state_messages && in_array( $module, $active ) ) {
2858
				$module_info = Jetpack::get_module( $module );
2859 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2860
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2861
					if ( $active_state = Jetpack::state( $state ) ) {
2862
						$active_state = explode( ',', $active_state );
2863
					} else {
2864
						$active_state = array();
2865
					}
2866
					$active_state[] = $module;
2867
					Jetpack::state( $state, implode( ',', $active_state ) );
2868
				}
2869
				continue;
2870
			}
2871
2872
			$file = Jetpack::get_module_path( $module );
2873
			if ( ! file_exists( $file ) ) {
2874
				continue;
2875
			}
2876
2877
			// we'll override this later if the plugin can be included without fatal error
2878
			if ( $redirect ) {
2879
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2880
			}
2881
2882
			if ( $send_state_messages ) {
2883
				Jetpack::state( 'error', 'module_activation_failed' );
2884
				Jetpack::state( 'module', $module );
2885
			}
2886
2887
			ob_start();
2888
			require_once $file;
2889
2890
			$active[] = $module;
2891
2892 View Code Duplication
			if ( $send_state_messages ) {
2893
2894
				$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2895
				if ( $active_state = Jetpack::state( $state ) ) {
2896
					$active_state = explode( ',', $active_state );
2897
				} else {
2898
					$active_state = array();
2899
				}
2900
				$active_state[] = $module;
2901
				Jetpack::state( $state, implode( ',', $active_state ) );
2902
			}
2903
2904
			Jetpack::update_active_modules( $active );
2905
2906
			ob_end_clean();
2907
		}
2908
2909
		if ( $send_state_messages ) {
2910
			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...
2911
			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...
2912
		}
2913
2914
		Jetpack::catch_errors( false );
2915
		/**
2916
		 * Fires when default modules are activated.
2917
		 *
2918
		 * @since 1.9.0
2919
		 *
2920
		 * @param string $min_version Minimum version number required to use modules.
2921
		 * @param string $max_version Maximum version number required to use modules.
2922
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2923
		 */
2924
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2925
	}
2926
2927
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2928
		/**
2929
		 * Fires before a module is activated.
2930
		 *
2931
		 * @since 2.6.0
2932
		 *
2933
		 * @param string $module Module slug.
2934
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2935
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2936
		 */
2937
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2938
2939
		$jetpack = Jetpack::init();
2940
2941
		if ( ! strlen( $module ) )
2942
			return false;
2943
2944
		if ( ! Jetpack::is_module( $module ) )
2945
			return false;
2946
2947
		// If it's already active, then don't do it again
2948
		$active = Jetpack::get_active_modules();
2949
		foreach ( $active as $act ) {
2950
			if ( $act == $module )
2951
				return true;
2952
		}
2953
2954
		$module_data = Jetpack::get_module( $module );
2955
2956
		if ( ! Jetpack::is_active() ) {
2957
			if ( ! Jetpack::is_development_mode() && ! Jetpack::is_onboarding() )
2958
				return false;
2959
2960
			// If we're not connected but in development mode, make sure the module doesn't require a connection
2961
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
2962
				return false;
2963
		}
2964
2965
		// Check and see if the old plugin is active
2966
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2967
			// Deactivate the old plugin
2968
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
2969
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
2970
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
2971
				Jetpack::state( 'deactivated_plugins', $module );
2972
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
2973
				exit;
2974
			}
2975
		}
2976
2977
		// Protect won't work with mis-configured IPs
2978
		if ( 'protect' === $module ) {
2979
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
2980
			if ( ! jetpack_protect_get_ip() ) {
2981
				Jetpack::state( 'message', 'protect_misconfigured_ip' );
2982
				return false;
2983
			}
2984
		}
2985
2986
		if ( ! Jetpack::active_plan_supports( $module ) ) {
2987
			return false;
2988
		}
2989
2990
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
2991
		Jetpack::state( 'module', $module );
2992
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
2993
2994
		Jetpack::catch_errors( true );
2995
		ob_start();
2996
		require Jetpack::get_module_path( $module );
2997
		/** This action is documented in class.jetpack.php */
2998
		do_action( 'jetpack_activate_module', $module );
2999
		$active[] = $module;
3000
		Jetpack::update_active_modules( $active );
3001
3002
		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...
3003
		ob_end_clean();
3004
		Jetpack::catch_errors( false );
3005
3006
		// A flag for Jump Start so it's not shown again. Only set if it hasn't been yet.
3007 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
3008
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
3009
3010
			//Jump start is being dismissed send data to MC Stats
3011
			$jetpack->stat( 'jumpstart', 'manual,'.$module );
3012
3013
			$jetpack->do_stats( 'server_side' );
3014
		}
3015
3016
		if ( $redirect ) {
3017
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3018
		}
3019
		if ( $exit ) {
3020
			exit;
3021
		}
3022
		return true;
3023
	}
3024
3025
	function activate_module_actions( $module ) {
3026
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3027
	}
3028
3029
	public static function deactivate_module( $module ) {
3030
		/**
3031
		 * Fires when a module is deactivated.
3032
		 *
3033
		 * @since 1.9.0
3034
		 *
3035
		 * @param string $module Module slug.
3036
		 */
3037
		do_action( 'jetpack_pre_deactivate_module', $module );
3038
3039
		$jetpack = Jetpack::init();
3040
3041
		$active = Jetpack::get_active_modules();
3042
		$new    = array_filter( array_diff( $active, (array) $module ) );
3043
3044
		// A flag for Jump Start so it's not shown again.
3045 View Code Duplication
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
3046
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
3047
3048
			//Jump start is being dismissed send data to MC Stats
3049
			$jetpack->stat( 'jumpstart', 'manual,deactivated-'.$module );
3050
3051
			$jetpack->do_stats( 'server_side' );
3052
		}
3053
3054
		return self::update_active_modules( $new );
3055
	}
3056
3057
	public static function enable_module_configurable( $module ) {
3058
		$module = Jetpack::get_module_slug( $module );
3059
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3060
	}
3061
3062
	public static function module_configuration_url( $module ) {
3063
		$module = Jetpack::get_module_slug( $module );
3064
		return Jetpack::admin_url( array( 'page' => 'jetpack', 'configure' => $module ) );
3065
	}
3066
3067
	public static function module_configuration_load( $module, $method ) {
3068
		$module = Jetpack::get_module_slug( $module );
3069
		add_action( 'jetpack_module_configuration_load_' . $module, $method );
3070
	}
3071
3072
	public static function module_configuration_head( $module, $method ) {
3073
		$module = Jetpack::get_module_slug( $module );
3074
		add_action( 'jetpack_module_configuration_head_' . $module, $method );
3075
	}
3076
3077
	public static function module_configuration_screen( $module, $method ) {
3078
		$module = Jetpack::get_module_slug( $module );
3079
		add_action( 'jetpack_module_configuration_screen_' . $module, $method );
3080
	}
3081
3082
	public static function module_configuration_activation_screen( $module, $method ) {
3083
		$module = Jetpack::get_module_slug( $module );
3084
		add_action( 'display_activate_module_setting_' . $module, $method );
3085
	}
3086
3087
/* Installation */
3088
3089
	public static function bail_on_activation( $message, $deactivate = true ) {
3090
?>
3091
<!doctype html>
3092
<html>
3093
<head>
3094
<meta charset="<?php bloginfo( 'charset' ); ?>">
3095
<style>
3096
* {
3097
	text-align: center;
3098
	margin: 0;
3099
	padding: 0;
3100
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3101
}
3102
p {
3103
	margin-top: 1em;
3104
	font-size: 18px;
3105
}
3106
</style>
3107
<body>
3108
<p><?php echo esc_html( $message ); ?></p>
3109
</body>
3110
</html>
3111
<?php
3112
		if ( $deactivate ) {
3113
			$plugins = get_option( 'active_plugins' );
3114
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3115
			$update  = false;
3116
			foreach ( $plugins as $i => $plugin ) {
3117
				if ( $plugin === $jetpack ) {
3118
					$plugins[$i] = false;
3119
					$update = true;
3120
				}
3121
			}
3122
3123
			if ( $update ) {
3124
				update_option( 'active_plugins', array_filter( $plugins ) );
3125
			}
3126
		}
3127
		exit;
3128
	}
3129
3130
	/**
3131
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3132
	 * @static
3133
	 */
3134
	public static function plugin_activation( $network_wide ) {
3135
		Jetpack_Options::update_option( 'activated', 1 );
3136
3137
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3138
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3139
		}
3140
3141
		if ( $network_wide )
3142
			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...
3143
3144
		// For firing one-off events (notices) immediately after activation
3145
		set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS );
3146
3147
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3148
3149
		Jetpack::plugin_initialize();
3150
	}
3151
3152
	public static function get_activation_source( $referer_url ) {
3153
3154
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3155
			return array( 'wp-cli', null );
3156
		}
3157
3158
		$referer = parse_url( $referer_url );
3159
3160
		$source_type = 'unknown';
3161
		$source_query = null;
3162
3163
		if ( ! is_array( $referer ) ) {
3164
			return array( $source_type, $source_query );
3165
		}
3166
3167
		$plugins_path = parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
3168
		$plugins_install_path = parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
3169
3170
		if ( isset( $referer['query'] ) ) {
3171
			parse_str( $referer['query'], $query_parts );
3172
		} else {
3173
			$query_parts = array();
3174
		}
3175
3176
		if ( $plugins_path === $referer['path'] ) {
3177
			$source_type = 'list';
3178
		} elseif ( $plugins_install_path === $referer['path'] ) {
3179
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3180
			switch( $tab ) {
3181
				case 'popular':
3182
					$source_type = 'popular';
3183
					break;
3184
				case 'recommended':
3185
					$source_type = 'recommended';
3186
					break;
3187
				case 'favorites':
3188
					$source_type = 'favorites';
3189
					break;
3190
				case 'search':
3191
					$source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3192
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3193
					break;
3194
				default:
3195
					$source_type = 'featured';
3196
			}
3197
		}
3198
3199
		return array( $source_type, $source_query );
3200
	}
3201
3202
	/**
3203
	 * Runs before bumping version numbers up to a new version
3204
	 * @param  string $version    Version:timestamp
3205
	 * @param  string $old_version Old Version:timestamp or false if not set yet.
3206
	 * @return null              [description]
3207
	 */
3208
	public static function do_version_bump( $version, $old_version ) {
3209
3210
		if ( ! $old_version ) { // For new sites
3211
			// Setting up jetpack manage
3212
			Jetpack::activate_manage();
3213
		}
3214
	}
3215
3216
	/**
3217
	 * Sets the internal version number and activation state.
3218
	 * @static
3219
	 */
3220
	public static function plugin_initialize() {
3221
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3222
			Jetpack_Options::update_option( 'activated', 2 );
3223
		}
3224
3225 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3226
			$version = $old_version = JETPACK__VERSION . ':' . time();
3227
			/** This action is documented in class.jetpack.php */
3228
			do_action( 'updating_jetpack_version', $version, false );
3229
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3230
		}
3231
3232
		Jetpack::load_modules();
3233
3234
		Jetpack_Options::delete_option( 'do_activate' );
3235
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3236
	}
3237
3238
	/**
3239
	 * Removes all connection options
3240
	 * @static
3241
	 */
3242
	public static function plugin_deactivation( ) {
3243
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
3244
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3245
			Jetpack_Network::init()->deactivate();
3246
		} else {
3247
			Jetpack::disconnect( false );
3248
			//Jetpack_Heartbeat::init()->deactivate();
3249
		}
3250
	}
3251
3252
	/**
3253
	 * Disconnects from the Jetpack servers.
3254
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3255
	 * @static
3256
	 */
3257
	public static function disconnect( $update_activated_state = true ) {
3258
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3259
		Jetpack::clean_nonces( true );
3260
3261
		// If the site is in an IDC because sync is not allowed,
3262
		// let's make sure to not disconnect the production site.
3263
		if ( ! self::validate_sync_error_idc_option() ) {
3264
			JetpackTracking::record_user_event( 'disconnect_site', array() );
3265
			Jetpack::load_xml_rpc_client();
3266
			$xml = new Jetpack_IXR_Client();
3267
			$xml->query( 'jetpack.deregister' );
3268
		}
3269
3270
		Jetpack_Options::delete_option(
3271
			array(
3272
				'blog_token',
3273
				'user_token',
3274
				'user_tokens',
3275
				'master_user',
3276
				'time_diff',
3277
				'fallback_no_verify_ssl_certs',
3278
			)
3279
		);
3280
3281
		Jetpack_IDC::clear_all_idc_options();
3282
		Jetpack_Options::delete_raw_option( 'jetpack_secrets' );
3283
3284
		if ( $update_activated_state ) {
3285
			Jetpack_Options::update_option( 'activated', 4 );
3286
		}
3287
3288
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3289
			// Check then record unique disconnection if site has never been disconnected previously
3290
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3291
				$jetpack_unique_connection['disconnected'] = 1;
3292
			} else {
3293
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3294
					//track unique disconnect
3295
					$jetpack = Jetpack::init();
3296
3297
					$jetpack->stat( 'connections', 'unique-disconnect' );
3298
					$jetpack->do_stats( 'server_side' );
3299
				}
3300
				// increment number of times disconnected
3301
				$jetpack_unique_connection['disconnected'] += 1;
3302
			}
3303
3304
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3305
		}
3306
3307
		// Delete cached connected user data
3308
		$transient_key = "jetpack_connected_user_data_" . get_current_user_id();
3309
		delete_transient( $transient_key );
3310
3311
		// Delete all the sync related data. Since it could be taking up space.
3312
		require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
3313
		Jetpack_Sync_Sender::get_instance()->uninstall();
3314
3315
		// Disable the Heartbeat cron
3316
		Jetpack_Heartbeat::init()->deactivate();
3317
	}
3318
3319
	/**
3320
	 * Unlinks the current user from the linked WordPress.com user
3321
	 */
3322
	public static function unlink_user( $user_id = null ) {
3323
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
3324
			return false;
3325
3326
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
3327
3328
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
3329
			return false;
3330
3331
		if ( ! isset( $tokens[ $user_id ] ) )
3332
			return false;
3333
3334
		Jetpack::load_xml_rpc_client();
3335
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
3336
		$xml->query( 'jetpack.unlink_user', $user_id );
3337
3338
		unset( $tokens[ $user_id ] );
3339
3340
		Jetpack_Options::update_option( 'user_tokens', $tokens );
3341
3342
		/**
3343
		 * Fires after the current user has been unlinked from WordPress.com.
3344
		 *
3345
		 * @since 4.1.0
3346
		 *
3347
		 * @param int $user_id The current user's ID.
3348
		 */
3349
		do_action( 'jetpack_unlinked_user', $user_id );
3350
3351
		return true;
3352
	}
3353
3354
	/**
3355
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3356
	 */
3357
	public static function try_registration() {
3358
		// The user has agreed to the TOS at some point by now.
3359
		Jetpack_Options::update_option( 'tos_agreed', true );
3360
3361
		// Let's get some testing in beta versions and such.
3362
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3363
			// Before attempting to connect, let's make sure that the domains are viable.
3364
			$domains_to_check = array_unique( array(
3365
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
3366
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
3367
			) );
3368
			foreach ( $domains_to_check as $domain ) {
3369
				$result = Jetpack_Data::is_usable_domain( $domain );
3370
				if ( is_wp_error( $result ) ) {
3371
					return $result;
3372
				}
3373
			}
3374
		}
3375
3376
		$result = Jetpack::register();
3377
3378
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3379
		if ( ! $result || is_wp_error( $result ) ) {
3380
			return $result;
3381
		} else {
3382
			return true;
3383
		}
3384
	}
3385
3386
	/**
3387
	 * Tracking an internal event log. Try not to put too much chaff in here.
3388
	 *
3389
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3390
	 */
3391
	public static function log( $code, $data = null ) {
3392
		// only grab the latest 200 entries
3393
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3394
3395
		// Append our event to the log
3396
		$log_entry = array(
3397
			'time'    => time(),
3398
			'user_id' => get_current_user_id(),
3399
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3400
			'code'    => $code,
3401
		);
3402
		// Don't bother storing it unless we've got some.
3403
		if ( ! is_null( $data ) ) {
3404
			$log_entry['data'] = $data;
3405
		}
3406
		$log[] = $log_entry;
3407
3408
		// Try add_option first, to make sure it's not autoloaded.
3409
		// @todo: Add an add_option method to Jetpack_Options
3410
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3411
			Jetpack_Options::update_option( 'log', $log );
3412
		}
3413
3414
		/**
3415
		 * Fires when Jetpack logs an internal event.
3416
		 *
3417
		 * @since 3.0.0
3418
		 *
3419
		 * @param array $log_entry {
3420
		 *	Array of details about the log entry.
3421
		 *
3422
		 *	@param string time Time of the event.
3423
		 *	@param int user_id ID of the user who trigerred the event.
3424
		 *	@param int blog_id Jetpack Blog ID.
3425
		 *	@param string code Unique name for the event.
3426
		 *	@param string data Data about the event.
3427
		 * }
3428
		 */
3429
		do_action( 'jetpack_log_entry', $log_entry );
3430
	}
3431
3432
	/**
3433
	 * Get the internal event log.
3434
	 *
3435
	 * @param $event (string) - only return the specific log events
3436
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3437
	 *
3438
	 * @return array of log events || WP_Error for invalid params
3439
	 */
3440
	public static function get_log( $event = false, $num = false ) {
3441
		if ( $event && ! is_string( $event ) ) {
3442
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
3443
		}
3444
3445
		if ( $num && ! is_numeric( $num ) ) {
3446
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
3447
		}
3448
3449
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3450
3451
		// If nothing set - act as it did before, otherwise let's start customizing the output
3452
		if ( ! $num && ! $event ) {
3453
			return $entire_log;
3454
		} else {
3455
			$entire_log = array_reverse( $entire_log );
3456
		}
3457
3458
		$custom_log_output = array();
3459
3460
		if ( $event ) {
3461
			foreach ( $entire_log as $log_event ) {
3462
				if ( $event == $log_event[ 'code' ] ) {
3463
					$custom_log_output[] = $log_event;
3464
				}
3465
			}
3466
		} else {
3467
			$custom_log_output = $entire_log;
3468
		}
3469
3470
		if ( $num ) {
3471
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3472
		}
3473
3474
		return $custom_log_output;
3475
	}
3476
3477
	/**
3478
	 * Log modification of important settings.
3479
	 */
3480
	public static function log_settings_change( $option, $old_value, $value ) {
3481
		switch( $option ) {
3482
			case 'jetpack_sync_non_public_post_stati':
3483
				self::log( $option, $value );
3484
				break;
3485
		}
3486
	}
3487
3488
	/**
3489
	 * Return stat data for WPCOM sync
3490
	 */
3491
	public static function get_stat_data( $encode = true, $extended = true ) {
3492
		$data = Jetpack_Heartbeat::generate_stats_array();
3493
3494
		if ( $extended ) {
3495
			$additional_data = self::get_additional_stat_data();
3496
			$data = array_merge( $data, $additional_data );
3497
		}
3498
3499
		if ( $encode ) {
3500
			return json_encode( $data );
3501
		}
3502
3503
		return $data;
3504
	}
3505
3506
	/**
3507
	 * Get additional stat data to sync to WPCOM
3508
	 */
3509
	public static function get_additional_stat_data( $prefix = '' ) {
3510
		$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...
3511
		$return["{$prefix}plugins-extra"]  = Jetpack::get_parsed_plugin_data();
3512
		$return["{$prefix}users"]          = (int) Jetpack::get_site_user_count();
3513
		$return["{$prefix}site-count"]     = 0;
3514
3515
		if ( function_exists( 'get_blog_count' ) ) {
3516
			$return["{$prefix}site-count"] = get_blog_count();
3517
		}
3518
		return $return;
3519
	}
3520
3521
	private static function get_site_user_count() {
3522
		global $wpdb;
3523
3524
		if ( function_exists( 'wp_is_large_network' ) ) {
3525
			if ( wp_is_large_network( 'users' ) ) {
3526
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3527
			}
3528
		}
3529 View Code Duplication
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3530
			// It wasn't there, so regenerate the data and save the transient
3531
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3532
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3533
		}
3534
		return $user_count;
3535
	}
3536
3537
	/* Admin Pages */
3538
3539
	function admin_init() {
3540
		// If the plugin is not connected, display a connect message.
3541
		if (
3542
			// the plugin was auto-activated and needs its candy
3543
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3544
		||
3545
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3546
			! Jetpack_Options::get_option( 'activated' )
3547
		) {
3548
			Jetpack::plugin_initialize();
3549
		}
3550
3551
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
3552
			Jetpack_Connection_Banner::init();
3553
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3554
			// Upgrade: 1.1 -> 1.1.1
3555
			// Check and see if host can verify the Jetpack servers' SSL certificate
3556
			$args = array();
3557
			Jetpack_Client::_wp_remote_request(
3558
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
3559
				$args,
3560
				true
3561
			);
3562
		} else if ( $this->can_display_jetpack_manage_notice() && ! Jetpack_Options::get_option( 'dismissed_manage_banner' ) ) {
3563
			// Show the notice on the Dashboard only for now
3564
			add_action( 'load-index.php', array( $this, 'prepare_manage_jetpack_notice' ) );
3565
		}
3566
3567
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3568
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3569
		}
3570
3571
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3572
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3573
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3574
3575
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
3576
			// Artificially throw errors in certain whitelisted cases during plugin activation
3577
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3578
		}
3579
3580
		// Jetpack Manage Activation Screen from .com
3581
		Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
3582
3583
		// Add custom column in wp-admin/users.php to show whether user is linked.
3584
		add_filter( 'manage_users_columns',       array( $this, 'jetpack_icon_user_connected' ) );
3585
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3586
		add_action( 'admin_print_styles',         array( $this, 'jetpack_user_col_style' ) );
3587
	}
3588
3589
	function admin_body_class( $admin_body_class = '' ) {
3590
		$classes = explode( ' ', trim( $admin_body_class ) );
3591
3592
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3593
3594
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3595
		return " $admin_body_class ";
3596
	}
3597
3598
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3599
		return $admin_body_class . ' jetpack-pagestyles ';
3600
	}
3601
3602
	/**
3603
	 * Call this function if you want the Big Jetpack Manage Notice to show up.
3604
	 *
3605
	 * @return null
3606
	 */
3607
	function prepare_manage_jetpack_notice() {
3608
3609
		add_action( 'admin_print_styles', array( $this, 'admin_banner_styles' ) );
3610
		add_action( 'admin_notices', array( $this, 'admin_jetpack_manage_notice' ) );
3611
	}
3612
3613
	function manage_activate_screen() {
3614
		include ( JETPACK__PLUGIN_DIR . 'modules/manage/activate-admin.php' );
3615
	}
3616
	/**
3617
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3618
	 * This function artificially throws errors for such cases (whitelisted).
3619
	 *
3620
	 * @param string $plugin The activated plugin.
3621
	 */
3622
	function throw_error_on_activate_plugin( $plugin ) {
3623
		$active_modules = Jetpack::get_active_modules();
3624
3625
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3626
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3627
			$throw = false;
3628
3629
			// Try and make sure it really was the stats plugin
3630
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3631
				if ( 'stats.php' == basename( $plugin ) ) {
3632
					$throw = true;
3633
				}
3634
			} else {
3635
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3636
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3637
					$throw = true;
3638
				}
3639
			}
3640
3641
			if ( $throw ) {
3642
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3643
			}
3644
		}
3645
	}
3646
3647
	function intercept_plugin_error_scrape_init() {
3648
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3649
	}
3650
3651
	function intercept_plugin_error_scrape( $action, $result ) {
3652
		if ( ! $result ) {
3653
			return;
3654
		}
3655
3656
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3657
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3658
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3659
			}
3660
		}
3661
	}
3662
3663
	function add_remote_request_handlers() {
3664
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
3665
		add_action( 'wp_ajax_nopriv_jetpack_update_file', array( $this, 'remote_request_handlers' ) );
3666
	}
3667
3668
	function remote_request_handlers() {
3669
		$action = current_filter();
0 ignored issues
show
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3670
3671
		switch ( current_filter() ) {
3672
		case 'wp_ajax_nopriv_jetpack_upload_file' :
3673
			$response = $this->upload_handler();
3674
			break;
3675
3676
		case 'wp_ajax_nopriv_jetpack_update_file' :
3677
			$response = $this->upload_handler( true );
3678
			break;
3679
		default :
3680
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
3681
			break;
3682
		}
3683
3684
		if ( ! $response ) {
3685
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
3686
		}
3687
3688
		if ( is_wp_error( $response ) ) {
3689
			$status_code       = $response->get_error_data();
3690
			$error             = $response->get_error_code();
3691
			$error_description = $response->get_error_message();
3692
3693
			if ( ! is_int( $status_code ) ) {
3694
				$status_code = 400;
3695
			}
3696
3697
			status_header( $status_code );
3698
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3699
		}
3700
3701
		status_header( 200 );
3702
		if ( true === $response ) {
3703
			exit;
3704
		}
3705
3706
		die( json_encode( (object) $response ) );
3707
	}
3708
3709
	/**
3710
	 * Uploads a file gotten from the global $_FILES.
3711
	 * If `$update_media_item` is true and `post_id` is defined
3712
	 * the attachment file of the media item (gotten through of the post_id)
3713
	 * will be updated instead of add a new one.
3714
	 *
3715
	 * @param  boolean $update_media_item - update media attachment
3716
	 * @return array - An array describing the uploadind files process
3717
	 */
3718
	function upload_handler( $update_media_item = false ) {
3719
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3720
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
3721
		}
3722
3723
		$user = wp_authenticate( '', '' );
3724
		if ( ! $user || is_wp_error( $user ) ) {
3725
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
3726
		}
3727
3728
		wp_set_current_user( $user->ID );
3729
3730
		if ( ! current_user_can( 'upload_files' ) ) {
3731
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
3732
		}
3733
3734
		if ( empty( $_FILES ) ) {
3735
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
3736
		}
3737
3738
		foreach ( array_keys( $_FILES ) as $files_key ) {
3739
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3740
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
3741
			}
3742
		}
3743
3744
		$media_keys = array_keys( $_FILES['media'] );
3745
3746
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
3747
		if ( ! $token || is_wp_error( $token ) ) {
3748
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
3749
		}
3750
3751
		$uploaded_files = array();
3752
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3753
		unset( $GLOBALS['post'] );
3754
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3755
			$file = array();
3756
			foreach ( $media_keys as $media_key ) {
3757
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3758
			}
3759
3760
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3761
3762
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3763
			if ( $hmac_provided !== $hmac_file ) {
3764
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3765
				continue;
3766
			}
3767
3768
			$_FILES['.jetpack.upload.'] = $file;
3769
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3770
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3771
				$post_id = 0;
3772
			}
3773
3774
			if ( $update_media_item ) {
3775
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3776
					return new Jetpack_Error( 'invalid_input', 'Media ID must be defined.', 400 );
3777
				}
3778
3779
				$media_array = $_FILES['media'];
3780
3781
				$file_array['name'] = $media_array['name'][0];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$file_array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $file_array = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
3782
				$file_array['type'] = $media_array['type'][0];
3783
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3784
				$file_array['error'] = $media_array['error'][0];
3785
				$file_array['size'] = $media_array['size'][0];
3786
3787
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3788
3789
				if ( is_wp_error( $edited_media_item ) ) {
3790
					return $edited_media_item;
3791
				}
3792
3793
				$response = (object) array(
3794
					'id'   => (string) $post_id,
3795
					'file' => (string) $edited_media_item->post_title,
3796
					'url'  => (string) wp_get_attachment_url( $post_id ),
3797
					'type' => (string) $edited_media_item->post_mime_type,
3798
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3799
				);
3800
3801
				return (array) array( $response );
3802
			}
3803
3804
			$attachment_id = media_handle_upload(
3805
				'.jetpack.upload.',
3806
				$post_id,
3807
				array(),
3808
				array(
3809
					'action' => 'jetpack_upload_file',
3810
				)
3811
			);
3812
3813
			if ( ! $attachment_id ) {
3814
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3815
			} elseif ( is_wp_error( $attachment_id ) ) {
3816
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3817
			} else {
3818
				$attachment = get_post( $attachment_id );
3819
				$uploaded_files[$index] = (object) array(
3820
					'id'   => (string) $attachment_id,
3821
					'file' => $attachment->post_title,
3822
					'url'  => wp_get_attachment_url( $attachment_id ),
3823
					'type' => $attachment->post_mime_type,
3824
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3825
				);
3826
				// Zip files uploads are not supported unless they are done for installation purposed
3827
				// lets delete them in case something goes wrong in this whole process
3828
				if ( 'application/zip' === $attachment->post_mime_type ) {
3829
					// Schedule a cleanup for 2 hours from now in case of failed install.
3830
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3831
				}
3832
			}
3833
		}
3834
		if ( ! is_null( $global_post ) ) {
3835
			$GLOBALS['post'] = $global_post;
3836
		}
3837
3838
		return $uploaded_files;
3839
	}
3840
3841
	/**
3842
	 * Add help to the Jetpack page
3843
	 *
3844
	 * @since Jetpack (1.2.3)
3845
	 * @return false if not the Jetpack page
3846
	 */
3847
	function admin_help() {
3848
		$current_screen = get_current_screen();
3849
3850
		// Overview
3851
		$current_screen->add_help_tab(
3852
			array(
3853
				'id'		=> 'home',
3854
				'title'		=> __( 'Home', 'jetpack' ),
3855
				'content'	=>
3856
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3857
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3858
					'<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>',
3859
			)
3860
		);
3861
3862
		// Screen Content
3863
		if ( current_user_can( 'manage_options' ) ) {
3864
			$current_screen->add_help_tab(
3865
				array(
3866
					'id'		=> 'settings',
3867
					'title'		=> __( 'Settings', 'jetpack' ),
3868
					'content'	=>
3869
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3870
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3871
						'<ol>' .
3872
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3873
							'<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>' .
3874
						'</ol>' .
3875
						'<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>'
3876
				)
3877
			);
3878
		}
3879
3880
		// Help Sidebar
3881
		$current_screen->set_help_sidebar(
3882
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3883
			'<p><a href="https://jetpack.com/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
3884
			'<p><a href="https://jetpack.com/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3885
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3886
		);
3887
	}
3888
3889
	function admin_menu_css() {
3890
		wp_enqueue_style( 'jetpack-icons' );
3891
	}
3892
3893
	function admin_menu_order() {
3894
		return true;
3895
	}
3896
3897 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3898
		$jp_menu_order = array();
3899
3900
		foreach ( $menu_order as $index => $item ) {
3901
			if ( $item != 'jetpack' ) {
3902
				$jp_menu_order[] = $item;
3903
			}
3904
3905
			if ( $index == 0 ) {
3906
				$jp_menu_order[] = 'jetpack';
3907
			}
3908
		}
3909
3910
		return $jp_menu_order;
3911
	}
3912
3913
	function admin_head() {
3914 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) )
3915
			/** This action is documented in class.jetpack-admin-page.php */
3916
			do_action( 'jetpack_module_configuration_head_' . $_GET['configure'] );
3917
	}
3918
3919
	function admin_banner_styles() {
3920
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3921
3922
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3923
			wp_register_style(
3924
				'jetpack-dops-style',
3925
				plugins_url( '_inc/build/admin.dops-style.css', JETPACK__PLUGIN_FILE ),
3926
				array(),
3927
				JETPACK__VERSION
3928
			);
3929
		}
3930
3931
		wp_enqueue_style(
3932
			'jetpack',
3933
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3934
			array( 'jetpack-dops-style' ),
3935
			 JETPACK__VERSION . '-20121016'
3936
		);
3937
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3938
		wp_style_add_data( 'jetpack', 'suffix', $min );
3939
	}
3940
3941
	function plugin_action_links( $actions ) {
3942
3943
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), 'Jetpack' ) );
3944
3945
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
3946
			return array_merge(
3947
				$jetpack_home,
3948
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3949
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
3950
				$actions
3951
				);
3952
			}
3953
3954
		return array_merge( $jetpack_home, $actions );
3955
	}
3956
3957
	/**
3958
	 * This is the first banner
3959
	 * It should be visible only to user that can update the option
3960
	 * Are not connected
3961
	 *
3962
	 * @return null
3963
	 */
3964
	function admin_jetpack_manage_notice() {
3965
		$screen = get_current_screen();
3966
3967
		// Don't show the connect notice on the jetpack settings page.
3968
		if ( ! in_array( $screen->base, array( 'dashboard' ) ) || $screen->is_network || $screen->action ) {
3969
			return;
3970
		}
3971
3972
		$opt_out_url = $this->opt_out_jetpack_manage_url();
3973
		$opt_in_url  = $this->opt_in_jetpack_manage_url();
3974
		/**
3975
		 * I think it would be great to have different wordsing depending on where you are
3976
		 * for example if we show the notice on dashboard and a different one if we show it on Plugins screen
3977
		 * etc..
3978
		 */
3979
3980
		?>
3981
		<div id="message" class="updated jp-banner">
3982
				<a href="<?php echo esc_url( $opt_out_url ); ?>" class="notice-dismiss" title="<?php esc_attr_e( 'Dismiss this notice', 'jetpack' ); ?>"></a>
3983
				<div class="jp-banner__description-container">
3984
					<h2 class="jp-banner__header"><?php esc_html_e( 'Jetpack Centralized Site Management', 'jetpack' ); ?></h2>
3985
					<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>
3986
					<p class="jp-banner__button-container">
3987
						<a href="<?php echo esc_url( $opt_in_url ); ?>" class="button button-primary" id="wpcom-connect"><?php _e( 'Activate Jetpack Manage', 'jetpack' ); ?></a>
3988
						<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>
3989
					</p>
3990
				</div>
3991
		</div>
3992
		<?php
3993
	}
3994
3995
	/**
3996
	 * Returns the url that the user clicks to remove the notice for the big banner
3997
	 * @return string
3998
	 */
3999
	function opt_out_jetpack_manage_url() {
4000
		$referer = '&_wp_http_referer=' . add_query_arg( '_wp_http_referer', null );
4001
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-out' . $referer ), 'jetpack_manage_banner_opt_out' );
4002
	}
4003
	/**
4004
	 * Returns the url that the user clicks to opt in to Jetpack Manage
4005
	 * @return string
4006
	 */
4007
	function opt_in_jetpack_manage_url() {
4008
		return wp_nonce_url( Jetpack::admin_url( 'jetpack-notice=jetpack-manage-opt-in' ), 'jetpack_manage_banner_opt_in' );
4009
	}
4010
4011
	function opt_in_jetpack_manage_notice() {
4012
		?>
4013
		<div class="wrap">
4014
			<div id="message" class="jetpack-message is-opt-in">
4015
				<?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' ); ?>
4016
			</div>
4017
		</div>
4018
		<?php
4019
4020
	}
4021
	/**
4022
	 * Determines whether to show the notice of not true = display notice
4023
	 * @return bool
4024
	 */
4025
	function can_display_jetpack_manage_notice() {
4026
		// never display the notice to users that can't do anything about it anyways
4027
		if( ! current_user_can( 'jetpack_manage_modules' ) )
4028
			return false;
4029
4030
		// don't display if we are in development more
4031
		if( Jetpack::is_development_mode() ) {
4032
			return false;
4033
		}
4034
		// don't display if the site is private
4035
		if(  ! Jetpack_Options::get_option( 'public' ) )
4036
			return false;
4037
4038
		/**
4039
		 * Should the Jetpack Remote Site Management notice be displayed.
4040
		 *
4041
		 * @since 3.3.0
4042
		 *
4043
		 * @param bool ! self::is_module_active( 'manage' ) Is the Manage module inactive.
4044
		 */
4045
		return apply_filters( 'can_display_jetpack_manage_notice', ! self::is_module_active( 'manage' ) );
4046
	}
4047
4048
	/*
4049
	 * Registration flow:
4050
	 * 1 - ::admin_page_load() action=register
4051
	 * 2 - ::try_registration()
4052
	 * 3 - ::register()
4053
	 *     - Creates jetpack_register option containing two secrets and a timestamp
4054
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
4055
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
4056
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
4057
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
4058
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
4059
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
4060
	 *       jetpack_id, jetpack_secret, jetpack_public
4061
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
4062
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
4063
	 * 5 - user logs in with WP.com account
4064
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
4065
	 *		- Jetpack_Client_Server::authorize()
4066
	 *		- Jetpack_Client_Server::get_token()
4067
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
4068
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
4069
	 *			- which responds with access_token, token_type, scope
4070
	 *		- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
4071
	 *		- Jetpack::activate_default_modules()
4072
	 *     		- Deactivates deprecated plugins
4073
	 *     		- Activates all default modules
4074
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
4075
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
4076
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
4077
	 *     Done!
4078
	 */
4079
4080
	/**
4081
	 * Handles the page load events for the Jetpack admin page
4082
	 */
4083
	function admin_page_load() {
4084
		$error = false;
4085
4086
		// Make sure we have the right body class to hook stylings for subpages off of.
4087
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
4088
4089
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
4090
			// Should only be used in intermediate redirects to preserve state across redirects
4091
			Jetpack::restate();
4092
		}
4093
4094
		if ( isset( $_GET['connect_url_redirect'] ) ) {
4095
			// User clicked in the iframe to link their accounts
4096
			if ( ! Jetpack::is_user_connected() ) {
4097
				$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
4098
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
4099
4100
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4101
				$connect_url = $this->build_connect_url( true, $redirect, $from );
4102
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
4103
4104
				if ( isset( $_GET['notes_iframe'] ) )
4105
					$connect_url .= '&notes_iframe';
4106
				wp_redirect( $connect_url );
4107
				exit;
4108
			} else {
4109
				if ( ! isset( $_GET['calypso_env'] ) ) {
4110
					Jetpack::state( 'message', 'already_authorized' );
4111
					wp_safe_redirect( Jetpack::admin_url() );
4112
					exit;
4113
				} else {
4114
					$connect_url = $this->build_connect_url( true, false, 'iframe' );
4115
					$connect_url .= '&already_authorized=true';
4116
					wp_redirect( $connect_url );
4117
					exit;
4118
				}
4119
			}
4120
		}
4121
4122
4123
		if ( isset( $_GET['action'] ) ) {
4124
			switch ( $_GET['action'] ) {
4125
			case 'authorize':
4126
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
4127
					Jetpack::state( 'message', 'already_authorized' );
4128
					wp_safe_redirect( Jetpack::admin_url() );
4129
					exit;
4130
				}
4131
				Jetpack::log( 'authorize' );
4132
				$client_server = new Jetpack_Client_Server;
4133
				$client_server->client_authorize();
4134
				exit;
4135
			case 'register' :
4136
				if ( ! current_user_can( 'jetpack_connect' ) ) {
4137
					$error = 'cheatin';
4138
					break;
4139
				}
4140
				check_admin_referer( 'jetpack-register' );
4141
				Jetpack::log( 'register' );
4142
				Jetpack::maybe_set_version_option();
4143
				$registered = Jetpack::try_registration();
4144
				if ( is_wp_error( $registered ) ) {
4145
					$error = $registered->get_error_code();
4146
					Jetpack::state( 'error', $error );
4147
					Jetpack::state( 'error', $registered->get_error_message() );
4148
					JetpackTracking::record_user_event( 'jpc_register_fail', array(
4149
						'error_code' => $error,
4150
						'error_message' => $registered->get_error_message()
4151
					) );
4152
					break;
4153
				}
4154
4155
				$from = isset( $_GET['from'] ) ? $_GET['from'] : false;
4156
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4157
4158
				JetpackTracking::record_user_event( 'jpc_register_success', array(
4159
					'from' => $from
4160
				) );
4161
4162
				$url = $this->build_connect_url( true, $redirect, $from );
4163
4164
				if ( ! empty( $_GET['onboarding'] ) ) {
4165
					$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4166
				}
4167
4168
				if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4169
					$url = add_query_arg( 'auth_approved', 'true', $url );
4170
				}
4171
4172
				wp_redirect( $url );
4173
				exit;
4174
			case 'activate' :
4175
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4176
					$error = 'cheatin';
4177
					break;
4178
				}
4179
4180
				$module = stripslashes( $_GET['module'] );
4181
				check_admin_referer( "jetpack_activate-$module" );
4182
				Jetpack::log( 'activate', $module );
4183
				if ( ! Jetpack::activate_module( $module ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \Jetpack::activate_module($module) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
4184
					Jetpack::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4185
				}
4186
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4187
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4188
				exit;
4189
			case 'activate_default_modules' :
4190
				check_admin_referer( 'activate_default_modules' );
4191
				Jetpack::log( 'activate_default_modules' );
4192
				Jetpack::restate();
4193
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4194
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4195
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4196
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
4197
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4198
				exit;
4199
			case 'disconnect' :
4200
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4201
					$error = 'cheatin';
4202
					break;
4203
				}
4204
4205
				check_admin_referer( 'jetpack-disconnect' );
4206
				Jetpack::log( 'disconnect' );
4207
				Jetpack::disconnect();
4208
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
4209
				exit;
4210
			case 'reconnect' :
4211
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4212
					$error = 'cheatin';
4213
					break;
4214
				}
4215
4216
				check_admin_referer( 'jetpack-reconnect' );
4217
				Jetpack::log( 'reconnect' );
4218
				$this->disconnect();
4219
				wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4220
				exit;
4221 View Code Duplication
			case 'deactivate' :
4222
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4223
					$error = 'cheatin';
4224
					break;
4225
				}
4226
4227
				$modules = stripslashes( $_GET['module'] );
4228
				check_admin_referer( "jetpack_deactivate-$modules" );
4229
				foreach ( explode( ',', $modules ) as $module ) {
4230
					Jetpack::log( 'deactivate', $module );
4231
					Jetpack::deactivate_module( $module );
4232
					Jetpack::state( 'message', 'module_deactivated' );
4233
				}
4234
				Jetpack::state( 'module', $modules );
4235
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4236
				exit;
4237
			case 'unlink' :
4238
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4239
				check_admin_referer( 'jetpack-unlink' );
4240
				Jetpack::log( 'unlink' );
4241
				$this->unlink_user();
4242
				Jetpack::state( 'message', 'unlinked' );
4243
				if ( 'sub-unlink' == $redirect ) {
4244
					wp_safe_redirect( admin_url() );
4245
				} else {
4246
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
4247
				}
4248
				exit;
4249
			case 'onboard' :
4250
				if ( ! current_user_can( 'manage_options' ) ) {
4251
					wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4252
				} else {
4253
					Jetpack::create_onboarding_token();
4254
					$url = $this->build_connect_url( true );
4255
4256
					if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4257
						$url = add_query_arg( 'onboarding', $token, $url );
4258
					}
4259
4260
					$calypso_env = ! empty( $_GET[ 'calypso_env' ] ) ? $_GET[ 'calypso_env' ] : false;
4261
					if ( $calypso_env ) {
4262
						$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4263
					}
4264
4265
					wp_redirect( $url );
4266
					exit;
4267
				}
4268
				exit;
4269
			default:
4270
				/**
4271
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4272
				 *
4273
				 * @since 2.6.0
4274
				 *
4275
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4276
				 */
4277
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4278
			}
4279
		}
4280
4281
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
4282
			self::activate_new_modules( true );
4283
		}
4284
4285
		$message_code = Jetpack::state( 'message' );
4286
		if ( Jetpack::state( 'optin-manage' ) ) {
4287
			$activated_manage = $message_code;
4288
			$message_code = 'jetpack-manage';
4289
		}
4290
4291
		switch ( $message_code ) {
4292
		case 'jetpack-manage':
4293
			$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>';
4294
			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...
4295
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
4296
			}
4297
			break;
4298
4299
		}
4300
4301
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
4302
4303
		if ( ! empty( $deactivated_plugins ) ) {
4304
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4305
			$deactivated_titles  = array();
4306
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4307
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
4308
					continue;
4309
				}
4310
4311
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
4312
			}
4313
4314
			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...
4315
				if ( $this->message ) {
4316
					$this->message .= "<br /><br />\n";
4317
				}
4318
4319
				$this->message .= wp_sprintf(
4320
					_n(
4321
						'Jetpack contains the most recent version of the old %l plugin.',
4322
						'Jetpack contains the most recent versions of the old %l plugins.',
4323
						count( $deactivated_titles ),
4324
						'jetpack'
4325
					),
4326
					$deactivated_titles
4327
				);
4328
4329
				$this->message .= "<br />\n";
4330
4331
				$this->message .= _n(
4332
					'The old version has been deactivated and can be removed from your site.',
4333
					'The old versions have been deactivated and can be removed from your site.',
4334
					count( $deactivated_titles ),
4335
					'jetpack'
4336
				);
4337
			}
4338
		}
4339
4340
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
4341
4342
		if ( $this->message || $this->error || $this->privacy_checks || $this->can_display_jetpack_manage_notice() ) {
4343
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4344
		}
4345
4346 View Code Duplication
		if ( isset( $_GET['configure'] ) && Jetpack::is_module( $_GET['configure'] ) && current_user_can( 'manage_options' ) ) {
4347
			/**
4348
			 * Fires when a module configuration page is loaded.
4349
			 * The dynamic part of the hook is the configure parameter from the URL.
4350
			 *
4351
			 * @since 1.1.0
4352
			 */
4353
			do_action( 'jetpack_module_configuration_load_' . $_GET['configure'] );
4354
		}
4355
4356
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4357
	}
4358
4359
	function admin_notices() {
4360
4361
		if ( $this->error ) {
4362
?>
4363
<div id="message" class="jetpack-message jetpack-err">
4364
	<div class="squeezer">
4365
		<h2><?php echo wp_kses( $this->error, array( 'a' => array( 'href' => array() ), 'small' => true, 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
4366
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
4367
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4368
<?php	endif; ?>
4369
	</div>
4370
</div>
4371
<?php
4372
		}
4373
4374
		if ( $this->message ) {
4375
?>
4376
<div id="message" class="jetpack-message">
4377
	<div class="squeezer">
4378
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
4379
	</div>
4380
</div>
4381
<?php
4382
		}
4383
4384
		if ( $this->privacy_checks ) :
4385
			$module_names = $module_slugs = array();
4386
4387
			$privacy_checks = explode( ',', $this->privacy_checks );
4388
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4389
			foreach ( $privacy_checks as $module_slug ) {
4390
				$module = Jetpack::get_module( $module_slug );
4391
				if ( ! $module ) {
4392
					continue;
4393
				}
4394
4395
				$module_slugs[] = $module_slug;
4396
				$module_names[] = "<strong>{$module['name']}</strong>";
4397
			}
4398
4399
			$module_slugs = join( ',', $module_slugs );
4400
?>
4401
<div id="message" class="jetpack-message jetpack-err">
4402
	<div class="squeezer">
4403
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4404
		<p><?php
4405
			echo wp_kses(
4406
				wptexturize(
4407
					wp_sprintf(
4408
						_nx(
4409
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4410
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4411
							count( $privacy_checks ),
4412
							'%l = list of Jetpack module/feature names',
4413
							'jetpack'
4414
						),
4415
						$module_names
4416
					)
4417
				),
4418
				array( 'strong' => true )
4419
			);
4420
4421
			echo "\n<br />\n";
4422
4423
			echo wp_kses(
4424
				sprintf(
4425
					_nx(
4426
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4427
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4428
						count( $privacy_checks ),
4429
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4430
						'jetpack'
4431
					),
4432
					wp_nonce_url(
4433
						Jetpack::admin_url(
4434
							array(
4435
								'page'   => 'jetpack',
4436
								'action' => 'deactivate',
4437
								'module' => urlencode( $module_slugs ),
4438
							)
4439
						),
4440
						"jetpack_deactivate-$module_slugs"
4441
					),
4442
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4443
				),
4444
				array( 'a' => array( 'href' => true, 'title' => true ) )
4445
			);
4446
		?></p>
4447
	</div>
4448
</div>
4449
<?php endif;
4450
	// only display the notice if the other stuff is not there
4451
	if( $this->can_display_jetpack_manage_notice() && !  $this->error && ! $this->message && ! $this->privacy_checks ) {
4452
		if( isset( $_GET['page'] ) && 'jetpack' != $_GET['page'] )
4453
			$this->opt_in_jetpack_manage_notice();
4454
		}
4455
	}
4456
4457
	/**
4458
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4459
	 */
4460
	function stat( $group, $detail ) {
4461
		if ( ! isset( $this->stats[ $group ] ) )
4462
			$this->stats[ $group ] = array();
4463
		$this->stats[ $group ][] = $detail;
4464
	}
4465
4466
	/**
4467
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4468
	 */
4469
	function do_stats( $method = '' ) {
4470
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4471
			foreach ( $this->stats as $group => $stats ) {
4472
				if ( is_array( $stats ) && count( $stats ) ) {
4473
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4474
					if ( 'server_side' === $method ) {
4475
						self::do_server_side_stat( $args );
4476
					} else {
4477
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4478
					}
4479
				}
4480
				unset( $this->stats[ $group ] );
4481
			}
4482
		}
4483
	}
4484
4485
	/**
4486
	 * Runs stats code for a one-off, server-side.
4487
	 *
4488
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4489
	 *
4490
	 * @return bool If it worked.
4491
	 */
4492
	static function do_server_side_stat( $args ) {
4493
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4494
		if ( is_wp_error( $response ) )
4495
			return false;
4496
4497
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
4498
			return false;
4499
4500
		return true;
4501
	}
4502
4503
	/**
4504
	 * Builds the stats url.
4505
	 *
4506
	 * @param $args array|string The arguments to append to the URL.
4507
	 *
4508
	 * @return string The URL to be pinged.
4509
	 */
4510
	static function build_stats_url( $args ) {
4511
		$defaults = array(
4512
			'v'    => 'wpcom2',
4513
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4514
		);
4515
		$args     = wp_parse_args( $args, $defaults );
4516
		/**
4517
		 * Filter the URL used as the Stats tracking pixel.
4518
		 *
4519
		 * @since 2.3.2
4520
		 *
4521
		 * @param string $url Base URL used as the Stats tracking pixel.
4522
		 */
4523
		$base_url = apply_filters(
4524
			'jetpack_stats_base_url',
4525
			'https://pixel.wp.com/g.gif'
4526
		);
4527
		$url      = add_query_arg( $args, $base_url );
4528
		return $url;
4529
	}
4530
4531
	static function translate_current_user_to_role() {
4532
		foreach ( self::$capability_translations as $role => $cap ) {
4533
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
4534
				return $role;
4535
			}
4536
		}
4537
4538
		return false;
4539
	}
4540
4541
	static function translate_user_to_role( $user ) {
4542
		foreach ( self::$capability_translations as $role => $cap ) {
4543
			if ( user_can( $user, $role ) || user_can( $user, $cap ) ) {
4544
				return $role;
4545
			}
4546
		}
4547
4548
		return false;
4549
    }
4550
4551
	static function translate_role_to_cap( $role ) {
4552
		if ( ! isset( self::$capability_translations[$role] ) ) {
4553
			return false;
4554
		}
4555
4556
		return self::$capability_translations[$role];
4557
	}
4558
4559
	static function sign_role( $role, $user_id = null ) {
4560
		if ( empty( $user_id ) ) {
4561
			$user_id = (int) get_current_user_id();
4562
		}
4563
4564
		if ( ! $user_id  ) {
4565
			return false;
4566
		}
4567
4568
		$token = Jetpack_Data::get_access_token();
4569
		if ( ! $token || is_wp_error( $token ) ) {
4570
			return false;
4571
		}
4572
4573
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4574
	}
4575
4576
4577
	/**
4578
	 * Builds a URL to the Jetpack connection auth page
4579
	 *
4580
	 * @since 3.9.5
4581
	 *
4582
	 * @param bool $raw If true, URL will not be escaped.
4583
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4584
	 *                              If string, will be a custom redirect.
4585
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4586
	 * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4587
	 *
4588
	 * @return string Connect URL
4589
	 */
4590
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4591
		$site_id = Jetpack_Options::get_option( 'id' );
4592
		$token = Jetpack_Options::get_option( 'blog_token' );
4593
4594
		if ( $register || ! $token || ! $site_id ) {
4595
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4596
4597
			if ( ! empty( $redirect ) ) {
4598
				$url = add_query_arg(
4599
					'redirect',
4600
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4601
					$url
4602
				);
4603
			}
4604
4605
			if( is_network_admin() ) {
4606
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4607
			}
4608
		} else {
4609
4610
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4611
			// because otherwise this logic can get us in to a loop.
4612
			$last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) );
4613
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4614
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4615
4616
				$response = Jetpack_Client::wpcom_json_api_request_as_blog(
4617
					sprintf( '/sites/%d', $site_id ) .'?force=wpcom',
4618
					'1.1'
4619
				);
4620
4621
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4622
					// Generating a register URL instead to refresh the existing token
4623
					return $this->build_connect_url( $raw, $redirect, $from, true );
4624
				}
4625
			}
4626
4627
			if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && include_once JETPACK__GLOTPRESS_LOCALES_PATH ) {
4628
				$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4629
			}
4630
4631
			$role = self::translate_current_user_to_role();
4632
			$signed_role = self::sign_role( $role );
4633
4634
			$user = wp_get_current_user();
4635
4636
			$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4637
			$redirect = $redirect
4638
				? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4639
				: $jetpack_admin_page;
4640
4641
			if( isset( $_REQUEST['is_multisite'] ) ) {
4642
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4643
			}
4644
4645
			$secrets = Jetpack::generate_secrets( 'authorize', false, 2 * HOUR_IN_SECONDS );
4646
4647
			$site_icon = ( function_exists( 'has_site_icon') && has_site_icon() )
4648
				? get_site_icon_url()
4649
				: false;
4650
4651
			/**
4652
			 * Filter the type of authorization.
4653
			 * 'calypso' completes authorization on wordpress.com/jetpack/connect
4654
			 * while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
4655
			 *
4656
			 * @since 4.3.3
4657
			 *
4658
			 * @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
4659
			 */
4660
			$auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );
4661
4662
			$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
4663
4664
			$args = urlencode_deep(
4665
				array(
4666
					'response_type' => 'code',
4667
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4668
					'redirect_uri'  => add_query_arg(
4669
						array(
4670
							'action'   => 'authorize',
4671
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4672
							'redirect' => urlencode( $redirect ),
4673
						),
4674
						esc_url( admin_url( 'admin.php?page=jetpack' ) )
4675
					),
4676
					'state'         => $user->ID,
4677
					'scope'         => $signed_role,
4678
					'user_email'    => $user->user_email,
4679
					'user_login'    => $user->user_login,
4680
					'is_active'     => Jetpack::is_active(),
4681
					'jp_version'    => JETPACK__VERSION,
4682
					'auth_type'     => $auth_type,
4683
					'secret'        => $secrets['secret_1'],
4684
					'locale'        => ( isset( $gp_locale ) && isset( $gp_locale->slug ) ) ? $gp_locale->slug : '',
4685
					'blogname'      => get_option( 'blogname' ),
4686
					'site_url'      => site_url(),
4687
					'home_url'      => home_url(),
4688
					'site_icon'     => $site_icon,
4689
					'site_lang'     => get_locale(),
4690
					'_ui'           => $tracks_identity['_ui'],
4691
					'_ut'           => $tracks_identity['_ut']
4692
				)
4693
			);
4694
4695
			self::apply_activation_source_to_args( $args );
4696
4697
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4698
		}
4699
4700
		if ( $from ) {
4701
			$url = add_query_arg( 'from', $from, $url );
4702
		}
4703
4704
4705
		if ( isset( $_GET['calypso_env'] ) ) {
4706
			$url = add_query_arg( 'calypso_env', sanitize_key( $_GET['calypso_env'] ), $url );
4707
		}
4708
4709
		return $raw ? $url : esc_url( $url );
4710
	}
4711
4712
	public static function apply_activation_source_to_args( &$args ) {
4713
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
4714
4715
		if ( $activation_source_name ) {
4716
			$args['_as'] = urlencode( $activation_source_name );
4717
		}
4718
4719
		if ( $activation_source_keyword ) {
4720
			$args['_ak'] = urlencode( $activation_source_keyword );
4721
		}
4722
	}
4723
4724
	function build_reconnect_url( $raw = false ) {
4725
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4726
		return $raw ? $url : esc_url( $url );
4727
	}
4728
4729
	public static function admin_url( $args = null ) {
4730
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4731
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4732
		return $url;
4733
	}
4734
4735
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4736
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4737
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4738
	}
4739
4740
	function dismiss_jetpack_notice() {
4741
4742
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4743
			return;
4744
		}
4745
4746
		switch( $_GET['jetpack-notice'] ) {
4747
			case 'dismiss':
4748
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4749
4750
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4751
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4752
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4753
				}
4754
				break;
4755 View Code Duplication
			case 'jetpack-manage-opt-out':
4756
4757
				if ( check_admin_referer( 'jetpack_manage_banner_opt_out' ) ) {
4758
					// Don't show the banner again
4759
4760
					Jetpack_Options::update_option( 'dismissed_manage_banner', true );
4761
					// redirect back to the page that had the notice
4762
					if ( wp_get_referer() ) {
4763
						wp_safe_redirect( wp_get_referer() );
4764
					} else {
4765
						// Take me to Jetpack
4766
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4767
					}
4768
				}
4769
				break;
4770 View Code Duplication
			case 'jetpack-protect-multisite-opt-out':
4771
4772
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4773
					// Don't show the banner again
4774
4775
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4776
					// redirect back to the page that had the notice
4777
					if ( wp_get_referer() ) {
4778
						wp_safe_redirect( wp_get_referer() );
4779
					} else {
4780
						// Take me to Jetpack
4781
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4782
					}
4783
				}
4784
				break;
4785
			case 'jetpack-manage-opt-in':
4786
				if ( check_admin_referer( 'jetpack_manage_banner_opt_in' ) ) {
4787
					// This makes sure that we are redirect to jetpack home so that we can see the Success Message.
4788
4789
					$redirection_url = Jetpack::admin_url();
4790
					remove_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4791
4792
					// Don't redirect form the Jetpack Setting Page
4793
					$referer_parsed = parse_url ( wp_get_referer() );
4794
					// check that we do have a wp_get_referer and the query paramater is set orderwise go to the Jetpack Home
4795
					if ( isset( $referer_parsed['query'] ) && false !== strpos( $referer_parsed['query'], 'page=jetpack_modules' ) ) {
4796
						// Take the user to Jetpack home except when on the setting page
4797
						$redirection_url = wp_get_referer();
4798
						add_action( 'jetpack_pre_activate_module',   array( Jetpack_Admin::init(), 'fix_redirect' ) );
4799
					}
4800
					// Also update the JSON API FULL MANAGEMENT Option
4801
					Jetpack::activate_module( 'manage', false, false );
4802
4803
					// Special Message when option in.
4804
					Jetpack::state( 'optin-manage', 'true' );
4805
					// Activate the Module if not activated already
4806
4807
					// Redirect properly
4808
					wp_safe_redirect( $redirection_url );
4809
4810
				}
4811
				break;
4812
		}
4813
	}
4814
4815
	public static function admin_screen_configure_module( $module_id ) {
4816
4817
		// User that doesn't have 'jetpack_configure_modules' will never end up here since Jetpack Landing Page woun't let them.
4818
		if ( ! in_array( $module_id, Jetpack::get_active_modules() ) && current_user_can( 'manage_options' ) ) {
4819
			if ( has_action( 'display_activate_module_setting_' . $module_id ) ) {
4820
				/**
4821
				 * Fires to diplay a custom module activation screen.
4822
				 *
4823
				 * To add a module actionation screen use Jetpack::module_configuration_activation_screen method.
4824
				 * Example: Jetpack::module_configuration_activation_screen( 'manage', array( $this, 'manage_activate_screen' ) );
4825
				 *
4826
				 * @module manage
4827
				 *
4828
				 * @since 3.8.0
4829
				 *
4830
				 * @param int $module_id Module ID.
4831
				 */
4832
				do_action( 'display_activate_module_setting_' . $module_id );
4833
			} else {
4834
				self::display_activate_module_link( $module_id );
4835
			}
4836
4837
			return false;
4838
		} ?>
4839
4840
		<div id="jp-settings-screen" style="position: relative">
4841
			<h3>
4842
			<?php
4843
				$module = Jetpack::get_module( $module_id );
4844
				printf( __( 'Configure %s', 'jetpack' ), $module['name'] );
4845
			?>
4846
			</h3>
4847
			<?php
4848
				/**
4849
				 * Fires within the displayed message when a feature configuation is updated.
4850
				 *
4851
				 * @since 3.4.0
4852
				 *
4853
				 * @param int $module_id Module ID.
4854
				 */
4855
				do_action( 'jetpack_notices_update_settings', $module_id );
4856
				/**
4857
				 * Fires when a feature configuation screen is loaded.
4858
				 * The dynamic part of the hook, $module_id, is the module ID.
4859
				 *
4860
				 * @since 1.1.0
4861
				 */
4862
				do_action( 'jetpack_module_configuration_screen_' . $module_id );
4863
			?>
4864
		</div><?php
4865
	}
4866
4867
	/**
4868
	 * Display link to activate the module to see the settings screen.
4869
	 * @param  string $module_id
4870
	 * @return null
4871
	 */
4872
	public static function display_activate_module_link( $module_id ) {
4873
4874
		$info =  Jetpack::get_module( $module_id );
4875
		$extra = '';
4876
		$activate_url = wp_nonce_url(
4877
				Jetpack::admin_url(
4878
					array(
4879
						'page'   => 'jetpack',
4880
						'action' => 'activate',
4881
						'module' => $module_id,
4882
					)
4883
				),
4884
				"jetpack_activate-$module_id"
4885
			);
4886
4887
		?>
4888
4889
		<div class="wrap configure-module">
4890
			<div id="jp-settings-screen">
4891
				<?php
4892
				if ( $module_id == 'json-api' ) {
4893
4894
					$info['name'] = esc_html__( 'Activate Site Management and JSON API', 'jetpack' );
4895
4896
					$activate_url = Jetpack::init()->opt_in_jetpack_manage_url();
4897
4898
					$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' );
4899
4900
					// $extra = __( 'To use Site Management, you need to first activate JSON API to allow remote management of your site. ', 'jetpack' );
4901
				} ?>
4902
4903
				<h3><?php echo esc_html( $info['name'] ); ?></h3>
4904
				<div class="narrow">
4905
					<p><?php echo  $info['description']; ?></p>
4906
					<?php if( $extra ) { ?>
4907
					<p><?php echo esc_html( $extra ); ?></p>
4908
					<?php } ?>
4909
					<p>
4910
						<?php
4911
						if( wp_get_referer() ) {
4912
							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() );
4913
						} else {
4914
							printf( __( '<a class="button-primary" href="%s">Activate Now</a>', 'jetpack' ) , $activate_url  );
4915
						} ?>
4916
					</p>
4917
				</div>
4918
4919
			</div>
4920
		</div>
4921
4922
		<?php
4923
	}
4924
4925
	public static function sort_modules( $a, $b ) {
4926
		if ( $a['sort'] == $b['sort'] )
4927
			return 0;
4928
4929
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4930
	}
4931
4932
	function ajax_recheck_ssl() {
4933
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
4934
		$result = Jetpack::permit_ssl( true );
4935
		wp_send_json( array(
4936
			'enabled' => $result,
4937
			'message' => get_transient( 'jetpack_https_test_message' )
4938
		) );
4939
	}
4940
4941
/* Client API */
4942
4943
	/**
4944
	 * Returns the requested Jetpack API URL
4945
	 *
4946
	 * @return string
4947
	 */
4948
	public static function api_url( $relative_url ) {
4949
		return trailingslashit( JETPACK__API_BASE . $relative_url  ) . JETPACK__API_VERSION . '/';
4950
	}
4951
4952
	/**
4953
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
4954
	 */
4955
	public static function fix_url_for_bad_hosts( $url ) {
4956
		if ( 0 !== strpos( $url, 'https://' ) ) {
4957
			return $url;
4958
		}
4959
4960
		switch ( JETPACK_CLIENT__HTTPS ) {
4961
			case 'ALWAYS' :
4962
				return $url;
4963
			case 'NEVER' :
4964
				return set_url_scheme( $url, 'http' );
4965
			// default : case 'AUTO' :
4966
		}
4967
4968
		// we now return the unmodified SSL URL by default, as a security precaution
4969
		return $url;
4970
	}
4971
4972
	/**
4973
	 * Create a random secret for validating onboarding payload
4974
	 *
4975
	 * @return string Secret token
4976
	 */
4977
	public static function create_onboarding_token() {
4978
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4979
			$token = wp_generate_password( 32, false );
4980
			Jetpack_Options::update_option( 'onboarding', $token );
4981
		}
4982
4983
		return $token;
4984
	}
4985
4986
	/**
4987
	 * Remove the onboarding token
4988
	 *
4989
	 * @return bool True on success, false on failure
4990
	 */
4991
	public static function invalidate_onboarding_token() {
4992
		return Jetpack_Options::delete_option( 'onboarding' );
4993
	}
4994
4995
	/**
4996
	 * Validate an onboarding token for a specific action
4997
	 *
4998
	 * @return boolean True if token/action pair is accepted, false if not
4999
	 */
5000
	public static function validate_onboarding_token_action( $token, $action ) {
5001
		// Compare tokens, bail if tokens do not match
5002
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
5003
			return false;
5004
		}
5005
5006
		// List of valid actions we can take
5007
		$valid_actions = array(
5008
			'/jetpack/v4/settings',
5009
		);
5010
5011
		// Whitelist the action
5012
		if ( ! in_array( $action, $valid_actions ) ) {
5013
			return false;
5014
		}
5015
5016
		return true;
5017
	}
5018
5019
	/**
5020
	 * Checks to see if the URL is using SSL to connect with Jetpack
5021
	 *
5022
	 * @since 2.3.3
5023
	 * @return boolean
5024
	 */
5025
	public static function permit_ssl( $force_recheck = false ) {
5026
		// Do some fancy tests to see if ssl is being supported
5027
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
5028
			$message = '';
5029
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
5030
				$ssl = 0;
5031
			} else {
5032
				switch ( JETPACK_CLIENT__HTTPS ) {
5033
					case 'NEVER':
5034
						$ssl = 0;
5035
						$message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' );
5036
						break;
5037
					case 'ALWAYS':
5038
					case 'AUTO':
5039
					default:
5040
						$ssl = 1;
5041
						break;
5042
				}
5043
5044
				// If it's not 'NEVER', test to see
5045
				if ( $ssl ) {
5046
					if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
5047
						$ssl = 0;
5048
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
5049
					} else {
5050
						$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
5051
						if ( is_wp_error( $response ) ) {
5052
							$ssl = 0;
5053
							$message = __( 'WordPress reports no SSL support', 'jetpack' );
5054
						} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
5055
							$ssl = 0;
5056
							$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
5057
						}
5058
					}
5059
				}
5060
			}
5061
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
5062
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
5063
		}
5064
5065
		return (bool) $ssl;
5066
	}
5067
5068
	/*
5069
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working.
5070
	 */
5071
	public function alert_auto_ssl_fail() {
5072
		if ( ! current_user_can( 'manage_options' ) )
5073
			return;
5074
5075
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
5076
		?>
5077
5078
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
5079
			<div class="jp-banner__content">
5080
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
5081
				<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>
5082
				<p>
5083
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
5084
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
5085
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
5086
				</p>
5087
				<p>
5088
					<?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' ),
5089
							esc_url( Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) ),
5090
							esc_url( 'https://jetpack.com/support/getting-started-with-jetpack/troubleshooting-tips/' ) ); ?>
5091
				</p>
5092
			</div>
5093
		</div>
5094
		<style>
5095
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
5096
		</style>
5097
		<script type="text/javascript">
5098
			jQuery( document ).ready( function( $ ) {
5099
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
5100
					var $this = $( this );
5101
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
5102
					$( '#jetpack-recheck-ssl-output' ).html( '' );
5103
					e.preventDefault();
5104
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
5105
					$.post( ajaxurl, data )
5106
					  .done( function( response ) {
5107
					  	if ( response.enabled ) {
5108
					  		$( '#jetpack-ssl-warning' ).hide();
5109
					  	} else {
5110
					  		this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
5111
					  		$( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
5112
					  	}
5113
					  }.bind( $this ) );
5114
				} );
5115
			} );
5116
		</script>
5117
5118
		<?php
5119
	}
5120
5121
	/**
5122
	 * Returns the Jetpack XML-RPC API
5123
	 *
5124
	 * @return string
5125
	 */
5126
	public static function xmlrpc_api_url() {
5127
		$base = preg_replace( '#(https?://[^?/]+)(/?.*)?$#', '\\1', JETPACK__API_BASE );
5128
		return untrailingslashit( $base ) . '/xmlrpc.php';
5129
	}
5130
5131
	/**
5132
	 * Creates two secret tokens and the end of life timestamp for them.
5133
	 *
5134
	 * Note these tokens are unique per call, NOT static per site for connecting.
5135
	 *
5136
	 * @since 2.6
5137
	 * @return array
5138
	 */
5139
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
5140
		if ( ! $user_id ) {
5141
			$user_id = get_current_user_id();
5142
		}
5143
5144
		$secret_name  = 'jetpack_' . $action . '_' . $user_id;
5145
		$secrets      = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5146
5147
		if (
5148
			isset( $secrets[ $secret_name ] ) &&
5149
			$secrets[ $secret_name ]['exp'] > time()
5150
		) {
5151
			return $secrets[ $secret_name ];
5152
		}
5153
5154
		$secret_value = array(
5155
			'secret_1'  => wp_generate_password( 32, false ),
5156
			'secret_2'  => wp_generate_password( 32, false ),
5157
			'exp'       => time() + $exp,
5158
		);
5159
5160
		$secrets[ $secret_name ] = $secret_value;
5161
5162
		Jetpack_Options::update_raw_option( 'jetpack_secrets', $secrets );
5163
		return $secrets[ $secret_name ];
5164
	}
5165
5166
	public static function get_secrets( $action, $user_id ) {
5167
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
5168
		$secrets = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5169
5170
		if ( ! isset( $secrets[ $secret_name ] ) ) {
5171
			return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
5172
		}
5173
5174
		if ( $secrets[ $secret_name ]['exp'] < time() ) {
5175
			self::delete_secrets( $action, $user_id );
5176
			return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
5177
		}
5178
5179
		return $secrets[ $secret_name ];
5180
	}
5181
5182
	public static function delete_secrets( $action, $user_id ) {
5183
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
5184
		$secrets = Jetpack_Options::get_raw_option( 'jetpack_secrets', array() );
5185
		if ( isset( $secrets[ $secret_name ] ) ) {
5186
			unset( $secrets[ $secret_name ] );
5187
			Jetpack_Options::update_raw_option( 'jetpack_secrets', $secrets );
5188
		}
5189
	}
5190
5191
	/**
5192
	 * Builds the timeout limit for queries talking with the wpcom servers.
5193
	 *
5194
	 * Based on local php max_execution_time in php.ini
5195
	 *
5196
	 * @since 2.6
5197
	 * @return int
5198
	 * @deprecated
5199
	 **/
5200
	public function get_remote_query_timeout_limit() {
5201
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
5202
		return Jetpack::get_max_execution_time();
5203
	}
5204
5205
	/**
5206
	 * Builds the timeout limit for queries talking with the wpcom servers.
5207
	 *
5208
	 * Based on local php max_execution_time in php.ini
5209
	 *
5210
	 * @since 5.4
5211
	 * @return int
5212
	 **/
5213
	public static function get_max_execution_time() {
5214
		$timeout = (int) ini_get( 'max_execution_time' );
5215
5216
		// Ensure exec time set in php.ini
5217
		if ( ! $timeout ) {
5218
			$timeout = 30;
5219
		}
5220
		return $timeout;
5221
	}
5222
5223
	/**
5224
	 * Sets a minimum request timeout, and returns the current timeout
5225
	 *
5226
	 * @since 5.4
5227
	 **/
5228
	public static function set_min_time_limit( $min_timeout ) {
5229
		$timeout = self::get_max_execution_time();
5230
		if ( $timeout < $min_timeout ) {
5231
			$timeout = $min_timeout;
5232
			set_time_limit( $timeout );
5233
		}
5234
		return $timeout;
5235
	}
5236
5237
5238
	/**
5239
	 * Takes the response from the Jetpack register new site endpoint and
5240
	 * verifies it worked properly.
5241
	 *
5242
	 * @since 2.6
5243
	 * @return string|Jetpack_Error A JSON object on success or Jetpack_Error on failures
5244
	 **/
5245
	public function validate_remote_register_response( $response ) {
5246
	  if ( is_wp_error( $response ) ) {
5247
			return new Jetpack_Error( 'register_http_request_failed', $response->get_error_message() );
5248
		}
5249
5250
		$code   = wp_remote_retrieve_response_code( $response );
5251
		$entity = wp_remote_retrieve_body( $response );
5252
		if ( $entity )
5253
			$registration_response = json_decode( $entity );
5254
		else
5255
			$registration_response = false;
5256
5257
		$code_type = intval( $code / 100 );
5258
		if ( 5 == $code_type ) {
5259
			return new Jetpack_Error( 'wpcom_5??', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5260
		} elseif ( 408 == $code ) {
5261
			return new Jetpack_Error( 'wpcom_408', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5262
		} elseif ( ! empty( $registration_response->error ) ) {
5263
			if ( 'xml_rpc-32700' == $registration_response->error && ! function_exists( 'xml_parser_create' ) ) {
5264
				$error_description = __( "PHP's XML extension is not available. Jetpack requires the XML extension to communicate with WordPress.com. Please contact your hosting provider to enable PHP's XML extension.", 'jetpack' );
5265
			} else {
5266
				$error_description = isset( $registration_response->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $registration_response->error_description ) : '';
5267
			}
5268
5269
			return new Jetpack_Error( (string) $registration_response->error, $error_description, $code );
5270
		} elseif ( 200 != $code ) {
5271
			return new Jetpack_Error( 'wpcom_bad_response', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
5272
		}
5273
5274
		// Jetpack ID error block
5275
		if ( empty( $registration_response->jetpack_id ) ) {
5276
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack' ), $entity ), $entity );
5277
		} elseif ( ! is_scalar( $registration_response->jetpack_id ) ) {
5278
			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 );
5279
		} elseif ( preg_match( '/[^0-9]/', $registration_response->jetpack_id ) ) {
5280
			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 );
5281
		}
5282
5283
	    return $registration_response;
5284
	}
5285
	/**
5286
	 * @return bool|WP_Error
5287
	 */
5288
	public static function register() {
5289
		JetpackTracking::record_user_event( 'jpc_register_begin' );
5290
		add_action( 'pre_update_jetpack_option_register', array( 'Jetpack_Options', 'delete_option' ) );
5291
		$secrets = Jetpack::generate_secrets( 'register' );
5292
5293 View Code Duplication
		if (
5294
			empty( $secrets['secret_1'] ) ||
5295
			empty( $secrets['secret_2'] ) ||
5296
			empty( $secrets['exp'] )
5297
		) {
5298
			return new Jetpack_Error( 'missing_secrets' );
5299
		}
5300
5301
		// better to try (and fail) to set a higher timeout than this system
5302
		// supports than to have register fail for more users than it should
5303
		$timeout = Jetpack::set_min_time_limit( 60 ) / 2;
5304
5305
		$gmt_offset = get_option( 'gmt_offset' );
5306
		if ( ! $gmt_offset ) {
5307
			$gmt_offset = 0;
5308
		}
5309
5310
		$stats_options = get_option( 'stats_options' );
5311
		$stats_id = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
5312
5313
		$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
5314
5315
		$args = array(
5316
			'method'  => 'POST',
5317
			'body'    => array(
5318
				'siteurl'         => site_url(),
5319
				'home'            => home_url(),
5320
				'gmt_offset'      => $gmt_offset,
5321
				'timezone_string' => (string) get_option( 'timezone_string' ),
5322
				'site_name'       => (string) get_option( 'blogname' ),
5323
				'secret_1'        => $secrets['secret_1'],
5324
				'secret_2'        => $secrets['secret_2'],
5325
				'site_lang'       => get_locale(),
5326
				'timeout'         => $timeout,
5327
				'stats_id'        => $stats_id,
5328
				'state'           => get_current_user_id(),
5329
				'_ui'             => $tracks_identity['_ui'],
5330
				'_ut'             => $tracks_identity['_ut'],
5331
				'jetpack_version' => JETPACK__VERSION
5332
			),
5333
			'headers' => array(
5334
				'Accept' => 'application/json',
5335
			),
5336
			'timeout' => $timeout,
5337
		);
5338
5339
		self::apply_activation_source_to_args( $args['body'] );
5340
5341
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'register' ) ), $args, true );
5342
5343
		// Make sure the response is valid and does not contain any Jetpack errors
5344
		$registration_details = Jetpack::init()->validate_remote_register_response( $response );
5345
		if ( is_wp_error( $registration_details ) ) {
5346
			return $registration_details;
5347
		} elseif ( ! $registration_details ) {
5348
			return new Jetpack_Error( 'unknown_error', __( 'Unknown error registering your Jetpack site', 'jetpack' ), wp_remote_retrieve_response_code( $response ) );
5349
		}
5350
5351 View Code Duplication
		if ( empty( $registration_details->jetpack_secret ) || ! is_string( $registration_details->jetpack_secret ) ) {
5352
			return new Jetpack_Error( 'jetpack_secret', '', wp_remote_retrieve_response_code( $response ) );
5353
		}
5354
5355
		if ( isset( $registration_details->jetpack_public ) ) {
5356
			$jetpack_public = (int) $registration_details->jetpack_public;
5357
		} else {
5358
			$jetpack_public = false;
5359
		}
5360
5361
		Jetpack_Options::update_options(
5362
			array(
5363
				'id'         => (int)    $registration_details->jetpack_id,
5364
				'blog_token' => (string) $registration_details->jetpack_secret,
5365
				'public'     => $jetpack_public,
5366
			)
5367
		);
5368
5369
		/**
5370
		 * Fires when a site is registered on WordPress.com.
5371
		 *
5372
		 * @since 3.7.0
5373
		 *
5374
		 * @param int $json->jetpack_id Jetpack Blog ID.
5375
		 * @param string $json->jetpack_secret Jetpack Blog Token.
5376
		 * @param int|bool $jetpack_public Is the site public.
5377
		 */
5378
		do_action( 'jetpack_site_registered', $registration_details->jetpack_id, $registration_details->jetpack_secret, $jetpack_public );
5379
5380
		// Initialize Jump Start for the first and only time.
5381
		if ( ! Jetpack_Options::get_option( 'jumpstart' ) ) {
5382
			Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
5383
5384
			$jetpack = Jetpack::init();
5385
5386
			$jetpack->stat( 'jumpstart', 'unique-views' );
5387
			$jetpack->do_stats( 'server_side' );
5388
		};
5389
5390
		return true;
5391
	}
5392
5393
	/**
5394
	 * If the db version is showing something other that what we've got now, bump it to current.
5395
	 *
5396
	 * @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...
5397
	 */
5398
	public static function maybe_set_version_option() {
5399
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5400
		if ( JETPACK__VERSION != $version ) {
5401
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5402
5403
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5404
				/** This action is documented in class.jetpack.php */
5405
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5406
			}
5407
5408
			return true;
5409
		}
5410
		return false;
5411
	}
5412
5413
/* Client Server API */
5414
5415
	/**
5416
	 * Loads the Jetpack XML-RPC client
5417
	 */
5418
	public static function load_xml_rpc_client() {
5419
		require_once ABSPATH . WPINC . '/class-IXR.php';
5420
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-ixr-client.php';
5421
	}
5422
5423
	/**
5424
	 * Resets the saved authentication state in between testing requests.
5425
	 */
5426
	public function reset_saved_auth_state() {
5427
		$this->xmlrpc_verification = null;
5428
		$this->rest_authentication_status = null;
5429
	}
5430
5431
	function verify_xml_rpc_signature() {
5432
		if ( $this->xmlrpc_verification ) {
5433
			return $this->xmlrpc_verification;
5434
		}
5435
5436
		// It's not for us
5437
		if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
5438
			return false;
5439
		}
5440
5441
		@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...
5442
		if (
5443
			empty( $token_key )
5444
		||
5445
			empty( $version ) || strval( JETPACK__API_VERSION ) !== $version
5446
		) {
5447
			return false;
5448
		}
5449
5450
		if ( '0' === $user_id ) {
5451
			$token_type = 'blog';
5452
			$user_id = 0;
5453
		} else {
5454
			$token_type = 'user';
5455
			if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
5456
				return false;
5457
			}
5458
			$user_id = (int) $user_id;
5459
5460
			$user = new WP_User( $user_id );
5461
			if ( ! $user || ! $user->exists() ) {
5462
				return false;
5463
			}
5464
		}
5465
5466
		$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...
5467
		if ( ! $token ) {
5468
			return false;
5469
		}
5470
5471
		$token_check = "$token_key.";
5472
		if ( ! hash_equals( substr( $token->secret, 0, strlen( $token_check ) ), $token_check ) ) {
5473
			return false;
5474
		}
5475
5476
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
5477
5478
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5479
		if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
5480
			$post_data   = $_POST;
5481
			$file_hashes = array();
5482
			foreach ( $post_data as $post_data_key => $post_data_value ) {
5483
				if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
5484
					continue;
5485
				}
5486
				$post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
5487
				$file_hashes[$post_data_key] = $post_data_value;
5488
			}
5489
5490
			foreach ( $file_hashes as $post_data_key => $post_data_value ) {
5491
				unset( $post_data["_jetpack_file_hmac_{$post_data_key}"] );
5492
				$post_data[$post_data_key] = $post_data_value;
5493
			}
5494
5495
			ksort( $post_data );
5496
5497
			$body = http_build_query( stripslashes_deep( $post_data ) );
5498
		} elseif ( is_null( $this->HTTP_RAW_POST_DATA ) ) {
5499
			$body = file_get_contents( 'php://input' );
5500
		} else {
5501
			$body = null;
5502
		}
5503
5504
		$signature = $jetpack_signature->sign_current_request(
5505
			array( 'body' => is_null( $body ) ? $this->HTTP_RAW_POST_DATA : $body, )
5506
		);
5507
5508
		if ( ! $signature ) {
5509
			return false;
5510
		} else if ( is_wp_error( $signature ) ) {
5511
			return $signature;
5512
		} else if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
5513
			return false;
5514
		}
5515
5516
		$timestamp = (int) $_GET['timestamp'];
5517
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5518
5519
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5520
			return false;
5521
		}
5522
5523
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5524
		if ( isset( $this->HTTP_RAW_POST_DATA ) || ! empty( $_GET['onboarding'] ) ) {
5525
			if ( ! empty( $_GET['onboarding'] ) ) {
5526
				$jpo = $_GET;
5527
			} else {
5528
				$jpo = json_decode( $this->HTTP_RAW_POST_DATA, true );
5529
			}
5530
5531
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5532
			$jpo_user = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5533
5534
			if (
5535
				isset( $jpo_user ) && isset( $jpo_token ) &&
5536
				is_email( $jpo_user ) && ctype_alnum( $jpo_token ) &&
5537
				isset( $_GET['rest_route'] ) &&
5538
				self::validate_onboarding_token_action( $jpo_token, $_GET['rest_route'] )
5539
			) {
5540
				$jpUser = get_user_by( 'email', $jpo_user );
5541
				if ( is_a( $jpUser, 'WP_User' ) ) {
5542
					wp_set_current_user( $jpUser->ID );
5543
					$user_can = is_multisite()
5544
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5545
						: current_user_can( 'manage_options' );
5546
					if ( $user_can ) {
5547
						$token_type = 'user';
5548
						$token->external_user_id = $jpUser->ID;
5549
					}
5550
				}
5551
			}
5552
		}
5553
5554
		$this->xmlrpc_verification = array(
5555
			'type'    => $token_type,
5556
			'user_id' => $token->external_user_id,
5557
		);
5558
5559
		return $this->xmlrpc_verification;
5560
	}
5561
5562
	/**
5563
	 * Authenticates XML-RPC and other requests from the Jetpack Server
5564
	 */
5565
	function authenticate_jetpack( $user, $username, $password ) {
5566
		if ( is_a( $user, 'WP_User' ) ) {
5567
			return $user;
5568
		}
5569
5570
		$token_details = $this->verify_xml_rpc_signature();
5571
5572
		if ( ! $token_details || is_wp_error( $token_details ) ) {
5573
			return $user;
5574
		}
5575
5576
		if ( 'user' !== $token_details['type'] ) {
5577
			return $user;
5578
		}
5579
5580
		if ( ! $token_details['user_id'] ) {
5581
			return $user;
5582
		}
5583
5584
		nocache_headers();
5585
5586
		return new WP_User( $token_details['user_id'] );
5587
	}
5588
5589
	// Authenticates requests from Jetpack server to WP REST API endpoints.
5590
	// Uses the existing XMLRPC request signing implementation.
5591
	function wp_rest_authenticate( $user ) {
5592
		if ( ! empty( $user ) ) {
5593
			// Another authentication method is in effect.
5594
			return $user;
5595
		}
5596
5597
		if ( ! isset( $_GET['_for'] ) || $_GET['_for'] !== 'jetpack' ) {
5598
			// Nothing to do for this authentication method.
5599
			return null;
5600
		}
5601
5602
		if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
5603
			// Nothing to do for this authentication method.
5604
			return null;
5605
		}
5606
5607
		// Ensure that we always have the request body available.  At this
5608
		// point, the WP REST API code to determine the request body has not
5609
		// run yet.  That code may try to read from 'php://input' later, but
5610
		// this can only be done once per request in PHP versions prior to 5.6.
5611
		// So we will go ahead and perform this read now if needed, and save
5612
		// the request body where both the Jetpack signature verification code
5613
		// and the WP REST API code can see it.
5614
		if ( ! isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ) {
5615
			$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents( 'php://input' );
5616
		}
5617
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5618
5619
		// Only support specific request parameters that have been tested and
5620
		// are known to work with signature verification.  A different method
5621
		// can be passed to the WP REST API via the '?_method=' parameter if
5622
		// needed.
5623
		if ( $_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
5624
			$this->rest_authentication_status = new WP_Error(
5625
				'rest_invalid_request',
5626
				__( 'This request method is not supported.', 'jetpack' ),
5627
				array( 'status' => 400 )
5628
			);
5629
			return null;
5630
		}
5631
		if ( $_SERVER['REQUEST_METHOD'] !== 'POST' && ! empty( $this->HTTP_RAW_POST_DATA ) ) {
5632
			$this->rest_authentication_status = new WP_Error(
5633
				'rest_invalid_request',
5634
				__( 'This request method does not support body parameters.', 'jetpack' ),
5635
				array( 'status' => 400 )
5636
			);
5637
			return null;
5638
		}
5639
5640
		$verified = $this->verify_xml_rpc_signature();
5641
5642
		if ( is_wp_error( $verified ) ) {
5643
			$this->rest_authentication_status = $verified;
5644
			return null;
5645
		}
5646
5647
		if (
5648
			$verified &&
5649
			isset( $verified['type'] ) &&
5650
			'user' === $verified['type'] &&
5651
			! empty( $verified['user_id'] )
5652
		) {
5653
			// Authentication successful.
5654
			$this->rest_authentication_status = true;
5655
			return $verified['user_id'];
5656
		}
5657
5658
		// Something else went wrong.  Probably a signature error.
5659
		$this->rest_authentication_status = new WP_Error(
5660
			'rest_invalid_signature',
5661
			__( 'The request is not signed correctly.', 'jetpack' ),
5662
			array( 'status' => 400 )
5663
		);
5664
		return null;
5665
	}
5666
5667
	/**
5668
	 * Report authentication status to the WP REST API.
5669
	 *
5670
	 * @param  WP_Error|mixed $result Error from another authentication handler, null if we should handle it, or another value if not
0 ignored issues
show
Bug introduced by
There is no parameter named $result. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5671
	 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5672
	 */
5673
	public function wp_rest_authentication_errors( $value ) {
5674
		if ( $value !== null ) {
5675
			return $value;
5676
		}
5677
		return $this->rest_authentication_status;
5678
	}
5679
5680
	function add_nonce( $timestamp, $nonce ) {
5681
		global $wpdb;
5682
		static $nonces_used_this_request = array();
5683
5684
		if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) {
5685
			return $nonces_used_this_request["$timestamp:$nonce"];
5686
		}
5687
5688
		// This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce
5689
		$timestamp = (int) $timestamp;
5690
		$nonce     = esc_sql( $nonce );
5691
5692
		// Raw query so we can avoid races: add_option will also update
5693
		$show_errors = $wpdb->show_errors( false );
5694
5695
		$old_nonce = $wpdb->get_row(
5696
			$wpdb->prepare( "SELECT * FROM `$wpdb->options` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
5697
		);
5698
5699
		if ( is_null( $old_nonce ) ) {
5700
			$return = $wpdb->query(
5701
				$wpdb->prepare(
5702
					"INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
5703
					"jetpack_nonce_{$timestamp}_{$nonce}",
5704
					time(),
5705
					'no'
5706
				)
5707
			);
5708
		} else {
5709
			$return = false;
5710
		}
5711
5712
		$wpdb->show_errors( $show_errors );
5713
5714
		$nonces_used_this_request["$timestamp:$nonce"] = $return;
5715
5716
		return $return;
5717
	}
5718
5719
	/**
5720
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5721
	 * Capture it here so we can verify the signature later.
5722
	 */
5723
	function xmlrpc_methods( $methods ) {
5724
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5725
		return $methods;
5726
	}
5727
5728
	function public_xmlrpc_methods( $methods ) {
5729
		if ( array_key_exists( 'wp.getOptions', $methods ) ) {
5730
			$methods['wp.getOptions'] = array( $this, 'jetpack_getOptions' );
5731
		}
5732
		return $methods;
5733
	}
5734
5735
	function jetpack_getOptions( $args ) {
5736
		global $wp_xmlrpc_server;
5737
5738
		$wp_xmlrpc_server->escape( $args );
5739
5740
		$username	= $args[1];
5741
		$password	= $args[2];
5742
5743
		if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
5744
			return $wp_xmlrpc_server->error;
5745
		}
5746
5747
		$options = array();
5748
		$user_data = $this->get_connected_user_data();
5749
		if ( is_array( $user_data ) ) {
5750
			$options['jetpack_user_id'] = array(
5751
				'desc'          => __( 'The WP.com user ID of the connected user', 'jetpack' ),
5752
				'readonly'      => true,
5753
				'value'         => $user_data['ID'],
5754
			);
5755
			$options['jetpack_user_login'] = array(
5756
				'desc'          => __( 'The WP.com username of the connected user', 'jetpack' ),
5757
				'readonly'      => true,
5758
				'value'         => $user_data['login'],
5759
			);
5760
			$options['jetpack_user_email'] = array(
5761
				'desc'          => __( 'The WP.com user email of the connected user', 'jetpack' ),
5762
				'readonly'      => true,
5763
				'value'         => $user_data['email'],
5764
			);
5765
			$options['jetpack_user_site_count'] = array(
5766
				'desc'          => __( 'The number of sites of the connected WP.com user', 'jetpack' ),
5767
				'readonly'      => true,
5768
				'value'         => $user_data['site_count'],
5769
			);
5770
		}
5771
		$wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
5772
		$args = stripslashes_deep( $args );
5773
		return $wp_xmlrpc_server->wp_getOptions( $args );
5774
	}
5775
5776
	function xmlrpc_options( $options ) {
5777
		$jetpack_client_id = false;
5778
		if ( self::is_active() ) {
5779
			$jetpack_client_id = Jetpack_Options::get_option( 'id' );
5780
		}
5781
		$options['jetpack_version'] = array(
5782
				'desc'          => __( 'Jetpack Plugin Version', 'jetpack' ),
5783
				'readonly'      => true,
5784
				'value'         => JETPACK__VERSION,
5785
		);
5786
5787
		$options['jetpack_client_id'] = array(
5788
				'desc'          => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack' ),
5789
				'readonly'      => true,
5790
				'value'         => $jetpack_client_id,
5791
		);
5792
		return $options;
5793
	}
5794
5795
	public static function clean_nonces( $all = false ) {
5796
		global $wpdb;
5797
5798
		$sql = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE %s";
5799
		$sql_args = array( $wpdb->esc_like( 'jetpack_nonce_' ) . '%' );
5800
5801
		if ( true !== $all ) {
5802
			$sql .= ' AND CAST( `option_value` AS UNSIGNED ) < %d';
5803
			$sql_args[] = time() - 3600;
5804
		}
5805
5806
		$sql .= ' ORDER BY `option_id` LIMIT 100';
5807
5808
		$sql = $wpdb->prepare( $sql, $sql_args );
5809
5810
		for ( $i = 0; $i < 1000; $i++ ) {
5811
			if ( ! $wpdb->query( $sql ) ) {
5812
				break;
5813
			}
5814
		}
5815
	}
5816
5817
	/**
5818
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5819
	 * SET: state( $key, $value );
5820
	 * GET: $value = state( $key );
5821
	 *
5822
	 * @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...
5823
	 * @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...
5824
	 * @param bool $restate private
5825
	 */
5826
	public static function state( $key = null, $value = null, $restate = false ) {
5827
		static $state = array();
5828
		static $path, $domain;
5829
		if ( ! isset( $path ) ) {
5830
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
5831
			$admin_url = Jetpack::admin_url();
5832
			$bits      = parse_url( $admin_url );
5833
5834
			if ( is_array( $bits ) ) {
5835
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5836
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5837
			} else {
5838
				$path = $domain = null;
5839
			}
5840
		}
5841
5842
		// Extract state from cookies and delete cookies
5843
		if ( isset( $_COOKIE[ 'jetpackState' ] ) && is_array( $_COOKIE[ 'jetpackState' ] ) ) {
5844
			$yum = $_COOKIE[ 'jetpackState' ];
5845
			unset( $_COOKIE[ 'jetpackState' ] );
5846
			foreach ( $yum as $k => $v ) {
5847
				if ( strlen( $v ) )
5848
					$state[ $k ] = $v;
5849
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5850
			}
5851
		}
5852
5853
		if ( $restate ) {
5854
			foreach ( $state as $k => $v ) {
5855
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5856
			}
5857
			return;
5858
		}
5859
5860
		// Get a state variable
5861
		if ( isset( $key ) && ! isset( $value ) ) {
5862
			if ( array_key_exists( $key, $state ) )
5863
				return $state[ $key ];
5864
			return null;
5865
		}
5866
5867
		// Set a state variable
5868
		if ( isset ( $key ) && isset( $value ) ) {
5869
			if( is_array( $value ) && isset( $value[0] ) ) {
5870
				$value = $value[0];
5871
			}
5872
			$state[ $key ] = $value;
5873
			setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5874
		}
5875
	}
5876
5877
	public static function restate() {
5878
		Jetpack::state( null, null, true );
5879
	}
5880
5881
	public static function check_privacy( $file ) {
5882
		static $is_site_publicly_accessible = null;
5883
5884
		if ( is_null( $is_site_publicly_accessible ) ) {
5885
			$is_site_publicly_accessible = false;
5886
5887
			Jetpack::load_xml_rpc_client();
5888
			$rpc = new Jetpack_IXR_Client();
5889
5890
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5891
			if ( $success ) {
5892
				$response = $rpc->getResponse();
5893
				if ( $response ) {
5894
					$is_site_publicly_accessible = true;
5895
				}
5896
			}
5897
5898
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5899
		}
5900
5901
		if ( $is_site_publicly_accessible ) {
5902
			return;
5903
		}
5904
5905
		$module_slug = self::get_module_slug( $file );
5906
5907
		$privacy_checks = Jetpack::state( 'privacy_checks' );
5908
		if ( ! $privacy_checks ) {
5909
			$privacy_checks = $module_slug;
5910
		} else {
5911
			$privacy_checks .= ",$module_slug";
5912
		}
5913
5914
		Jetpack::state( 'privacy_checks', $privacy_checks );
5915
	}
5916
5917
	/**
5918
	 * Helper method for multicall XMLRPC.
5919
	 */
5920
	public static function xmlrpc_async_call() {
5921
		global $blog_id;
5922
		static $clients = array();
5923
5924
		$client_blog_id = is_multisite() ? $blog_id : 0;
5925
5926
		if ( ! isset( $clients[$client_blog_id] ) ) {
5927
			Jetpack::load_xml_rpc_client();
5928
			$clients[$client_blog_id] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER, ) );
5929
			if ( function_exists( 'ignore_user_abort' ) ) {
5930
				ignore_user_abort( true );
5931
			}
5932
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5933
		}
5934
5935
		$args = func_get_args();
5936
5937
		if ( ! empty( $args[0] ) ) {
5938
			call_user_func_array( array( $clients[$client_blog_id], 'addCall' ), $args );
5939
		} elseif ( is_multisite() ) {
5940
			foreach ( $clients as $client_blog_id => $client ) {
5941
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5942
					continue;
5943
				}
5944
5945
				$switch_success = switch_to_blog( $client_blog_id, true );
5946
				if ( ! $switch_success ) {
5947
					continue;
5948
				}
5949
5950
				flush();
5951
				$client->query();
5952
5953
				restore_current_blog();
5954
			}
5955
		} else {
5956
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5957
				flush();
5958
				$clients[0]->query();
5959
			}
5960
		}
5961
	}
5962
5963
	public static function staticize_subdomain( $url ) {
5964
5965
		// Extract hostname from URL
5966
		$host = parse_url( $url, PHP_URL_HOST );
5967
5968
		// Explode hostname on '.'
5969
		$exploded_host = explode( '.', $host );
5970
5971
		// Retrieve the name and TLD
5972
		if ( count( $exploded_host ) > 1 ) {
5973
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
5974
			$tld = $exploded_host[ count( $exploded_host ) - 1 ];
5975
			// Rebuild domain excluding subdomains
5976
			$domain = $name . '.' . $tld;
5977
		} else {
5978
			$domain = $host;
5979
		}
5980
		// Array of Automattic domains
5981
		$domain_whitelist = array( 'wordpress.com', 'wp.com' );
5982
5983
		// Return $url if not an Automattic domain
5984
		if ( ! in_array( $domain, $domain_whitelist ) ) {
5985
			return $url;
5986
		}
5987
5988
		if ( is_ssl() ) {
5989
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
5990
		}
5991
5992
		srand( crc32( basename( $url ) ) );
5993
		$static_counter = rand( 0, 2 );
5994
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
5995
5996
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
5997
	}
5998
5999
/* JSON API Authorization */
6000
6001
	/**
6002
	 * Handles the login action for Authorizing the JSON API
6003
	 */
6004
	function login_form_json_api_authorization() {
6005
		$this->verify_json_api_authorization_request();
6006
6007
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
6008
6009
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
6010
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
6011
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
6012
	}
6013
6014
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
6015
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
6016
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
6017
			return $url;
6018
		}
6019
6020
		$parsed_url = parse_url( $url );
6021
		$url = strtok( $url, '?' );
6022
		$url = "$url?{$_SERVER['QUERY_STRING']}";
6023
		if ( ! empty( $parsed_url['query'] ) )
6024
			$url .= "&{$parsed_url['query']}";
6025
6026
		return $url;
6027
	}
6028
6029
	// Make sure the POSTed request is handled by the same action
6030
	function preserve_action_in_login_form_for_json_api_authorization() {
6031
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
6032
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
6033
	}
6034
6035
	// If someone logs in to approve API access, store the Access Code in usermeta
6036
	function store_json_api_authorization_token( $user_login, $user ) {
6037
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
6038
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
6039
		$token = wp_generate_password( 32, false );
6040
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
6041
	}
6042
6043
	// Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access
6044
	function allow_wpcom_public_api_domain( $domains ) {
6045
		$domains[] = 'public-api.wordpress.com';
6046
		return $domains;
6047
	}
6048
6049
	// Add all wordpress.com environments to the safe redirect whitelist
6050
	function allow_wpcom_environments( $domains ) {
6051
		$domains[] = 'wordpress.com';
6052
		$domains[] = 'wpcalypso.wordpress.com';
6053
		$domains[] = 'horizon.wordpress.com';
6054
		$domains[] = 'calypso.localhost';
6055
		return $domains;
6056
	}
6057
6058
	// Add the Access Code details to the public-api.wordpress.com redirect
6059
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
6060
		return add_query_arg(
6061
			urlencode_deep(
6062
				array(
6063
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
6064
					'jetpack-user-id' => (int) $user->ID,
6065
					'jetpack-state'   => $this->json_api_authorization_request['state'],
6066
				)
6067
			),
6068
			$redirect_to
6069
		);
6070
	}
6071
6072
6073
	/**
6074
	 * Verifies the request by checking the signature
6075
	 *
6076
	 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
6077
	 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
6078
	 *
6079
	 * @param null|array $environment
6080
	 */
6081
	function verify_json_api_authorization_request( $environment = null ) {
6082
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
6083
6084
		$environment = is_null( $environment )
6085
			? $_REQUEST
6086
			: $environment;
6087
6088
		list( $envToken, $envVersion, $envUserId ) = explode( ':', $environment['token'] );
0 ignored issues
show
Unused Code introduced by
The assignment to $envToken is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $envVersion is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
6089
		$token = Jetpack_Data::get_access_token( $envUserId );
6090
		if ( ! $token || empty( $token->secret ) ) {
6091
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' , 'jetpack' ) );
6092
		}
6093
6094
		$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' );
6095
6096
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
6097
6098
		if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
6099
			$signature = $jetpack_signature->sign_request(
6100
				$environment['token'],
6101
				$environment['timestamp'],
6102
				$environment['nonce'],
6103
				'',
6104
				'GET',
6105
				$environment['jetpack_json_api_original_query'],
6106
				null,
6107
				true
6108
			);
6109
		} else {
6110
			$signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) );
6111
		}
6112
6113
		if ( ! $signature ) {
6114
			wp_die( $die_error );
6115
		} else if ( is_wp_error( $signature ) ) {
6116
			wp_die( $die_error );
6117
		} else if ( ! hash_equals( $signature, $environment['signature'] ) ) {
6118
			if ( is_ssl() ) {
6119
				// 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
6120
				$signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) );
6121
				if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
6122
					wp_die( $die_error );
6123
				}
6124
			} else {
6125
				wp_die( $die_error );
6126
			}
6127
		}
6128
6129
		$timestamp = (int) $environment['timestamp'];
6130
		$nonce     = stripslashes( (string) $environment['nonce'] );
6131
6132
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
6133
			// De-nonce the nonce, at least for 5 minutes.
6134
			// 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)
6135
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
6136
			if ( $old_nonce_time < time() - 300 ) {
6137
				wp_die( __( 'The authorization process expired.  Please go back and try again.' , 'jetpack' ) );
6138
			}
6139
		}
6140
6141
		$data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
6142
		$data_filters = array(
6143
			'state'        => 'opaque',
6144
			'client_id'    => 'int',
6145
			'client_title' => 'string',
6146
			'client_image' => 'url',
6147
		);
6148
6149
		foreach ( $data_filters as $key => $sanitation ) {
6150
			if ( ! isset( $data->$key ) ) {
6151
				wp_die( $die_error );
6152
			}
6153
6154
			switch ( $sanitation ) {
6155
			case 'int' :
6156
				$this->json_api_authorization_request[$key] = (int) $data->$key;
6157
				break;
6158
			case 'opaque' :
6159
				$this->json_api_authorization_request[$key] = (string) $data->$key;
6160
				break;
6161
			case 'string' :
6162
				$this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() );
6163
				break;
6164
			case 'url' :
6165
				$this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key );
6166
				break;
6167
			}
6168
		}
6169
6170
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
6171
			wp_die( $die_error );
6172
		}
6173
	}
6174
6175
	function login_message_json_api_authorization( $message ) {
6176
		return '<p class="message">' . sprintf(
6177
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' , 'jetpack' ),
6178
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
6179
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
6180
	}
6181
6182
	/**
6183
	 * Get $content_width, but with a <s>twist</s> filter.
6184
	 */
6185
	public static function get_content_width() {
6186
		$content_width = isset( $GLOBALS['content_width'] ) ? $GLOBALS['content_width'] : false;
6187
		/**
6188
		 * Filter the Content Width value.
6189
		 *
6190
		 * @since 2.2.3
6191
		 *
6192
		 * @param string $content_width Content Width value.
6193
		 */
6194
		return apply_filters( 'jetpack_content_width', $content_width );
6195
	}
6196
6197
	/**
6198
	 * Pings the WordPress.com Mirror Site for the specified options.
6199
	 *
6200
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6201
	 *
6202
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6203
	 */
6204
	public function get_cloud_site_options( $option_names ) {
6205
		$option_names = array_filter( (array) $option_names, 'is_string' );
6206
6207
		Jetpack::load_xml_rpc_client();
6208
		$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
6209
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
6210
		if ( $xml->isError() ) {
6211
			return array(
6212
				'error_code' => $xml->getErrorCode(),
6213
				'error_msg'  => $xml->getErrorMessage(),
6214
			);
6215
		}
6216
		$cloud_site_options = $xml->getResponse();
6217
6218
		return $cloud_site_options;
6219
	}
6220
6221
	/**
6222
	 * Checks if the site is currently in an identity crisis.
6223
	 *
6224
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6225
	 */
6226
	public static function check_identity_crisis() {
6227
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || ! self::validate_sync_error_idc_option() ) {
6228
			return false;
6229
		}
6230
6231
		return Jetpack_Options::get_option( 'sync_error_idc' );
6232
	}
6233
6234
	/**
6235
	 * Checks whether the home and siteurl specifically are whitelisted
6236
	 * Written so that we don't have re-check $key and $value params every time
6237
	 * we want to check if this site is whitelisted, for example in footer.php
6238
	 *
6239
	 * @since  3.8.0
6240
	 * @return bool True = already whitelisted False = not whitelisted
6241
	 */
6242
	public static function is_staging_site() {
6243
		$is_staging = false;
6244
6245
		$known_staging = array(
6246
			'urls' => array(
6247
				'#\.staging\.wpengine\.com$#i', // WP Engine
6248
				'#\.staging\.kinsta\.com$#i',   // Kinsta.com
6249
				),
6250
			'constants' => array(
6251
				'IS_WPE_SNAPSHOT',      // WP Engine
6252
				'KINSTA_DEV_ENV',       // Kinsta.com
6253
				'WPSTAGECOACH_STAGING', // WP Stagecoach
6254
				'JETPACK_STAGING_MODE', // Generic
6255
				)
6256
			);
6257
		/**
6258
		 * Filters the flags of known staging sites.
6259
		 *
6260
		 * @since 3.9.0
6261
		 *
6262
		 * @param array $known_staging {
6263
		 *     An array of arrays that each are used to check if the current site is staging.
6264
		 *     @type array $urls      URLs of staging sites in regex to check against site_url.
6265
		 *     @type array $constants PHP constants of known staging/developement environments.
6266
		 *  }
6267
		 */
6268
		$known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
6269
6270
		if ( isset( $known_staging['urls'] ) ) {
6271
			foreach ( $known_staging['urls'] as $url ){
6272
				if ( preg_match( $url, site_url() ) ) {
6273
					$is_staging = true;
6274
					break;
6275
				}
6276
			}
6277
		}
6278
6279
		if ( isset( $known_staging['constants'] ) ) {
6280
			foreach ( $known_staging['constants'] as $constant ) {
6281
				if ( defined( $constant ) && constant( $constant ) ) {
6282
					$is_staging = true;
6283
				}
6284
			}
6285
		}
6286
6287
		// Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
6288
		if ( ! $is_staging && self::validate_sync_error_idc_option() ) {
6289
			$is_staging = true;
6290
		}
6291
6292
		/**
6293
		 * Filters is_staging_site check.
6294
		 *
6295
		 * @since 3.9.0
6296
		 *
6297
		 * @param bool $is_staging If the current site is a staging site.
6298
		 */
6299
		return apply_filters( 'jetpack_is_staging_site', $is_staging );
6300
	}
6301
6302
	/**
6303
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6304
	 *
6305
	 * @since 4.4.0
6306
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6307
	 *
6308
	 * @return bool
6309
	 */
6310
	public static function validate_sync_error_idc_option() {
6311
		$is_valid = false;
6312
6313
		$idc_allowed = get_transient( 'jetpack_idc_allowed' );
6314
		if ( false === $idc_allowed ) {
6315
			$response = wp_remote_get( 'https://jetpack.com/is-idc-allowed/' );
6316
			if ( 200 === (int) wp_remote_retrieve_response_code( $response ) ) {
6317
				$json = json_decode( wp_remote_retrieve_body( $response ) );
6318
				$idc_allowed = isset( $json, $json->result ) && $json->result ? '1' : '0';
6319
				$transient_duration = HOUR_IN_SECONDS;
6320
			} else {
6321
				// If the request failed for some reason, then assume IDC is allowed and set shorter transient.
6322
				$idc_allowed = '1';
6323
				$transient_duration = 5 * MINUTE_IN_SECONDS;
6324
			}
6325
6326
			set_transient( 'jetpack_idc_allowed', $idc_allowed, $transient_duration );
6327
		}
6328
6329
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
6330
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
6331
		if ( $idc_allowed && $sync_error && self::sync_idc_optin() ) {
6332
			$local_options = self::get_sync_error_idc_option();
6333
			if ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
6334
				$is_valid = true;
6335
			}
6336
		}
6337
6338
		/**
6339
		 * Filters whether the sync_error_idc option is valid.
6340
		 *
6341
		 * @since 4.4.0
6342
		 *
6343
		 * @param bool $is_valid If the sync_error_idc is valid or not.
6344
		 */
6345
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
6346
6347
		if ( ! $idc_allowed || ( ! $is_valid && $sync_error ) ) {
6348
			// Since the option exists, and did not validate, delete it
6349
			Jetpack_Options::delete_option( 'sync_error_idc' );
6350
		}
6351
6352
		return $is_valid;
6353
	}
6354
6355
	/**
6356
	 * Normalizes a url by doing three things:
6357
	 *  - Strips protocol
6358
	 *  - Strips www
6359
	 *  - Adds a trailing slash
6360
	 *
6361
	 * @since 4.4.0
6362
	 * @param string $url
6363
	 * @return WP_Error|string
6364
	 */
6365
	public static function normalize_url_protocol_agnostic( $url ) {
6366
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6367
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
6368
			return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) );
6369
		}
6370
6371
		// Strip www and protocols
6372
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6373
		return $url;
6374
	}
6375
6376
	/**
6377
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6378
	 *
6379
	 * @since 4.4.0
6380
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6381
	 *
6382
	 * @param array $response
6383
	 * @return array Array of the local urls, wpcom urls, and error code
6384
	 */
6385
	public static function get_sync_error_idc_option( $response = array() ) {
6386
		// Since the local options will hit the database directly, store the values
6387
		// in a transient to allow for autoloading and caching on subsequent views.
6388
		$local_options = get_transient( 'jetpack_idc_local' );
6389
		if ( false === $local_options ) {
6390
			require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-functions.php';
6391
			$local_options = array(
6392
				'home'    => Jetpack_Sync_Functions::home_url(),
6393
				'siteurl' => Jetpack_Sync_Functions::site_url(),
6394
			);
6395
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
6396
		}
6397
6398
		$options = array_merge( $local_options, $response );
6399
6400
		$returned_values = array();
6401
		foreach( $options as $key => $option ) {
6402
			if ( 'error_code' === $key ) {
6403
				$returned_values[ $key ] = $option;
6404
				continue;
6405
			}
6406
6407
			if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) {
6408
				continue;
6409
			}
6410
6411
			$returned_values[ $key ] = $normalized_url;
6412
		}
6413
6414
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
6415
6416
		return $returned_values;
6417
	}
6418
6419
	/**
6420
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6421
	 * If set to true, the site will be put into staging mode.
6422
	 *
6423
	 * @since 4.3.2
6424
	 * @return bool
6425
	 */
6426
	public static function sync_idc_optin() {
6427
		if ( Jetpack_Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
6428
			$default = Jetpack_Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
6429
		} else {
6430
			$default = ! Jetpack_Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
6431
		}
6432
6433
		/**
6434
		 * Allows sites to optin to IDC mitigation which blocks the site from syncing to WordPress.com when the home
6435
		 * URL or site URL do not match what WordPress.com expects. The default value is either false, or the value of
6436
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
6437
		 *
6438
		 * @since 4.3.2
6439
		 *
6440
		 * @param bool $default Whether the site is opted in to IDC mitigation.
6441
		 */
6442
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
6443
	}
6444
6445
	/**
6446
	 * Maybe Use a .min.css stylesheet, maybe not.
6447
	 *
6448
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6449
	 */
6450
	public static function maybe_min_asset( $url, $path, $plugin ) {
6451
		// Short out on things trying to find actual paths.
6452
		if ( ! $path || empty( $plugin ) ) {
6453
			return $url;
6454
		}
6455
6456
		$path = ltrim( $path, '/' );
6457
6458
		// Strip out the abspath.
6459
		$base = dirname( plugin_basename( $plugin ) );
6460
6461
		// Short out on non-Jetpack assets.
6462
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6463
			return $url;
6464
		}
6465
6466
		// File name parsing.
6467
		$file              = "{$base}/{$path}";
6468
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6469
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6470
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6471
		$extension         = array_shift( $file_name_parts_r );
6472
6473
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6474
			// Already pointing at the minified version.
6475
			if ( 'min' === $file_name_parts_r[0] ) {
6476
				return $url;
6477
			}
6478
6479
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6480
			if ( file_exists( $min_full_path ) ) {
6481
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6482
				// If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6483
				if ( 'css' === $extension ) {
6484
					$key = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6485
					self::$min_assets[ $key ] = $path;
6486
				}
6487
			}
6488
		}
6489
6490
		return $url;
6491
	}
6492
6493
	/**
6494
	 * If the asset is minified, let's flag .min as the suffix.
6495
	 *
6496
	 * Attached to `style_loader_src` filter.
6497
	 *
6498
	 * @param string $tag The tag that would link to the external asset.
0 ignored issues
show
Bug introduced by
There is no parameter named $tag. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6499
	 * @param string $handle The registered handle of the script in question.
6500
	 * @param string $href The url of the asset in question.
0 ignored issues
show
Bug introduced by
There is no parameter named $href. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6501
	 */
6502
	public static function set_suffix_on_min( $src, $handle ) {
6503
		if ( false === strpos( $src, '.min.css' ) ) {
6504
			return $src;
6505
		}
6506
6507
		if ( ! empty( self::$min_assets ) ) {
6508
			foreach ( self::$min_assets as $file => $path ) {
6509
				if ( false !== strpos( $src, $file ) ) {
6510
					wp_style_add_data( $handle, 'suffix', '.min' );
6511
					return $src;
6512
				}
6513
			}
6514
		}
6515
6516
		return $src;
6517
	}
6518
6519
	/**
6520
	 * Maybe inlines a stylesheet.
6521
	 *
6522
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6523
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6524
	 *
6525
	 * Attached to `style_loader_tag` filter.
6526
	 *
6527
	 * @param string $tag The tag that would link to the external asset.
6528
	 * @param string $handle The registered handle of the script in question.
6529
	 *
6530
	 * @return string
6531
	 */
6532
	public static function maybe_inline_style( $tag, $handle ) {
6533
		global $wp_styles;
6534
		$item = $wp_styles->registered[ $handle ];
6535
6536
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6537
			return $tag;
6538
		}
6539
6540
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6541
			$href = $matches[1];
6542
			// Strip off query string
6543
			if ( $pos = strpos( $href, '?' ) ) {
6544
				$href = substr( $href, 0, $pos );
6545
			}
6546
			// Strip off fragment
6547
			if ( $pos = strpos( $href, '#' ) ) {
6548
				$href = substr( $href, 0, $pos );
6549
			}
6550
		} else {
6551
			return $tag;
6552
		}
6553
6554
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6555
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6556
			return $tag;
6557
		}
6558
6559
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6560
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6561
			// And this isn't the pass that actually deals with the RTL version...
6562
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6563
				// Short out, as the RTL version will deal with it in a moment.
6564
				return $tag;
6565
			}
6566
		}
6567
6568
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6569
		$css  = Jetpack::absolutize_css_urls( file_get_contents( $file ), $href );
6570
		if ( $css ) {
6571
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6572
			if ( empty( $item->extra['after'] ) ) {
6573
				wp_add_inline_style( $handle, $css );
6574
			} else {
6575
				array_unshift( $item->extra['after'], $css );
6576
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6577
			}
6578
		}
6579
6580
		return $tag;
6581
	}
6582
6583
	/**
6584
	 * Loads a view file from the views
6585
	 *
6586
	 * Data passed in with the $data parameter will be available in the
6587
	 * template file as $data['value']
6588
	 *
6589
	 * @param string $template - Template file to load
6590
	 * @param array $data - Any data to pass along to the template
6591
	 * @return boolean - If template file was found
6592
	 **/
6593
	public function load_view( $template, $data = array() ) {
6594
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6595
6596
		if( file_exists( $views_dir . $template ) ) {
6597
			require_once( $views_dir . $template );
6598
			return true;
6599
		}
6600
6601
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6602
		return false;
6603
	}
6604
6605
	/**
6606
	 * Throws warnings for deprecated hooks to be removed from Jetpack
6607
	 */
6608
	public function deprecated_hooks() {
6609
		global $wp_filter;
6610
6611
		/*
6612
		 * Format:
6613
		 * deprecated_filter_name => replacement_name
6614
		 *
6615
		 * If there is no replacement, use null for replacement_name
6616
		 */
6617
		$deprecated_list = array(
6618
			'jetpack_bail_on_shortcode'                              => 'jetpack_shortcodes_to_include',
6619
			'wpl_sharing_2014_1'                                     => null,
6620
			'jetpack-tools-to-include'                               => 'jetpack_tools_to_include',
6621
			'jetpack_identity_crisis_options_to_check'               => null,
6622
			'update_option_jetpack_single_user_site'                 => null,
6623
			'audio_player_default_colors'                            => null,
6624
			'add_option_jetpack_featured_images_enabled'             => null,
6625
			'add_option_jetpack_update_details'                      => null,
6626
			'add_option_jetpack_updates'                             => null,
6627
			'add_option_jetpack_network_name'                        => null,
6628
			'add_option_jetpack_network_allow_new_registrations'     => null,
6629
			'add_option_jetpack_network_add_new_users'               => null,
6630
			'add_option_jetpack_network_site_upload_space'           => null,
6631
			'add_option_jetpack_network_upload_file_types'           => null,
6632
			'add_option_jetpack_network_enable_administration_menus' => null,
6633
			'add_option_jetpack_is_multi_site'                       => null,
6634
			'add_option_jetpack_is_main_network'                     => null,
6635
			'add_option_jetpack_main_network_site'                   => null,
6636
			'jetpack_sync_all_registered_options'                    => null,
6637
			'jetpack_has_identity_crisis'                            => 'jetpack_sync_error_idc_validation',
6638
			'jetpack_is_post_mailable'                               => null,
6639
			'jetpack_seo_site_host'                                  => null,
6640
			'jetpack_installed_plugin'                               => 'jetpack_plugin_installed',
6641
			'jetpack_holiday_snow_option_name'                       => null,
6642
			'jetpack_holiday_chance_of_snow'                         => null,
6643
			'jetpack_holiday_snow_js_url'                            => null,
6644
			'jetpack_is_holiday_snow_season'                         => null,
6645
			'jetpack_holiday_snow_option_updated'                    => null,
6646
			'jetpack_holiday_snowing'                                => null,
6647
			'jetpack_sso_auth_cookie_expirtation'                    => 'jetpack_sso_auth_cookie_expiration',
6648
			'jetpack_cache_plans'                                    => null,
6649
			'jetpack_updated_theme'                                  => 'jetpack_updated_themes',
6650
			'jetpack_lazy_images_skip_image_with_atttributes'        => 'jetpack_lazy_images_skip_image_with_attributes',
6651
			'jetpack_enable_site_verification'                       => null,
6652
		);
6653
6654
		// This is a silly loop depth. Better way?
6655
		foreach( $deprecated_list AS $hook => $hook_alt ) {
6656
			if ( has_action( $hook ) ) {
6657
				foreach( $wp_filter[ $hook ] AS $func => $values ) {
6658
					foreach( $values AS $hooked ) {
6659
						if ( is_callable( $hooked['function'] ) ) {
6660
							$function_name = 'an anonymous function';
6661
						} else {
6662
							$function_name = $hooked['function'];
6663
						}
6664
						_deprecated_function( $hook . ' used for ' . $function_name, null, $hook_alt );
6665
					}
6666
				}
6667
			}
6668
		}
6669
	}
6670
6671
	/**
6672
	 * Converts any url in a stylesheet, to the correct absolute url.
6673
	 *
6674
	 * Considerations:
6675
	 *  - Normal, relative URLs     `feh.png`
6676
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6677
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6678
	 *  - Absolute URLs             `http://domain.com/feh.png`
6679
	 *  - Domain root relative URLs `/feh.png`
6680
	 *
6681
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6682
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6683
	 *
6684
	 * @return mixed|string
6685
	 */
6686
	public static function absolutize_css_urls( $css, $css_file_url ) {
6687
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6688
		$css_dir = dirname( $css_file_url );
6689
		$p       = parse_url( $css_dir );
6690
		$domain  = sprintf(
6691
					'%1$s//%2$s%3$s%4$s',
6692
					isset( $p['scheme'] )           ? "{$p['scheme']}:" : '',
6693
					isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6694
					$p['host'],
6695
					isset( $p['port'] )             ? ":{$p['port']}" : ''
6696
				);
6697
6698
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6699
			$find = $replace = array();
6700
			foreach ( $matches as $match ) {
6701
				$url = trim( $match['path'], "'\" \t" );
6702
6703
				// If this is a data url, we don't want to mess with it.
6704
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6705
					continue;
6706
				}
6707
6708
				// If this is an absolute or protocol-agnostic url,
6709
				// we don't want to mess with it.
6710
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6711
					continue;
6712
				}
6713
6714
				switch ( substr( $url, 0, 1 ) ) {
6715
					case '/':
6716
						$absolute = $domain . $url;
6717
						break;
6718
					default:
6719
						$absolute = $css_dir . '/' . $url;
6720
				}
6721
6722
				$find[]    = $match[0];
6723
				$replace[] = sprintf( 'url("%s")', $absolute );
6724
			}
6725
			$css = str_replace( $find, $replace, $css );
6726
		}
6727
6728
		return $css;
6729
	}
6730
6731
	/**
6732
	 * This methods removes all of the registered css files on the front end
6733
	 * from Jetpack in favor of using a single file. In effect "imploding"
6734
	 * all the files into one file.
6735
	 *
6736
	 * Pros:
6737
	 * - Uses only ONE css asset connection instead of 15
6738
	 * - Saves a minimum of 56k
6739
	 * - Reduces server load
6740
	 * - Reduces time to first painted byte
6741
	 *
6742
	 * Cons:
6743
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6744
	 *		should not cause any issues with themes.
6745
	 * - Plugins/themes dequeuing styles no longer do anything. See
6746
	 *		jetpack_implode_frontend_css filter for a workaround
6747
	 *
6748
	 * For some situations developers may wish to disable css imploding and
6749
	 * instead operate in legacy mode where each file loads seperately and
6750
	 * can be edited individually or dequeued. This can be accomplished with
6751
	 * the following line:
6752
	 *
6753
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6754
	 *
6755
	 * @since 3.2
6756
	 **/
6757
	public function implode_frontend_css( $travis_test = false ) {
6758
		$do_implode = true;
6759
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6760
			$do_implode = false;
6761
		}
6762
6763
		/**
6764
		 * Allow CSS to be concatenated into a single jetpack.css file.
6765
		 *
6766
		 * @since 3.2.0
6767
		 *
6768
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6769
		 */
6770
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6771
6772
		// Do not use the imploded file when default behaviour was altered through the filter
6773
		if ( ! $do_implode ) {
6774
			return;
6775
		}
6776
6777
		// We do not want to use the imploded file in dev mode, or if not connected
6778
		if ( Jetpack::is_development_mode() || ! self::is_active() ) {
6779
			if ( ! $travis_test ) {
6780
				return;
6781
			}
6782
		}
6783
6784
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6785
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6786
			return;
6787
		}
6788
6789
		/*
6790
		 * Now we assume Jetpack is connected and able to serve the single
6791
		 * file.
6792
		 *
6793
		 * In the future there will be a check here to serve the file locally
6794
		 * or potentially from the Jetpack CDN
6795
		 *
6796
		 * For now:
6797
		 * - Enqueue a single imploded css file
6798
		 * - Zero out the style_loader_tag for the bundled ones
6799
		 * - Be happy, drink scotch
6800
		 */
6801
6802
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6803
6804
		$version = Jetpack::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6805
6806
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6807
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6808
	}
6809
6810
	function concat_remove_style_loader_tag( $tag, $handle ) {
6811
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6812
			$tag = '';
6813
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6814
				$tag = "<!-- `" . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6815
			}
6816
		}
6817
6818
		return $tag;
6819
	}
6820
6821
	/*
6822
	 * Check the heartbeat data
6823
	 *
6824
	 * Organizes the heartbeat data by severity.  For example, if the site
6825
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6826
	 *
6827
	 * Data will be added to "caution" array, if it either:
6828
	 *  - Out of date Jetpack version
6829
	 *  - Out of date WP version
6830
	 *  - Out of date PHP version
6831
	 *
6832
	 * $return array $filtered_data
6833
	 */
6834
	public static function jetpack_check_heartbeat_data() {
6835
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6836
6837
		$good    = array();
6838
		$caution = array();
6839
		$bad     = array();
6840
6841
		foreach ( $raw_data as $stat => $value ) {
6842
6843
			// Check jetpack version
6844
			if ( 'version' == $stat ) {
6845
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6846
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__VERSION;
6847
					continue;
6848
				}
6849
			}
6850
6851
			// Check WP version
6852
			if ( 'wp-version' == $stat ) {
6853
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6854
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_WP_VERSION;
6855
					continue;
6856
				}
6857
			}
6858
6859
			// Check PHP version
6860
			if ( 'php-version' == $stat ) {
6861
				if ( version_compare( PHP_VERSION, '5.2.4', '<' ) ) {
6862
					$caution[ $stat ] = $value . " - min supported is 5.2.4";
6863
					continue;
6864
				}
6865
			}
6866
6867
			// Check ID crisis
6868
			if ( 'identitycrisis' == $stat ) {
6869
				if ( 'yes' == $value ) {
6870
					$bad[ $stat ] = $value;
6871
					continue;
6872
				}
6873
			}
6874
6875
			// The rest are good :)
6876
			$good[ $stat ] = $value;
6877
		}
6878
6879
		$filtered_data = array(
6880
			'good'    => $good,
6881
			'caution' => $caution,
6882
			'bad'     => $bad
6883
		);
6884
6885
		return $filtered_data;
6886
	}
6887
6888
6889
	/*
6890
	 * This method is used to organize all options that can be reset
6891
	 * without disconnecting Jetpack.
6892
	 *
6893
	 * It is used in class.jetpack-cli.php to reset options
6894
	 *
6895
	 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
6896
	 *
6897
	 * @return array of options to delete.
6898
	 */
6899
	public static function get_jetpack_options_for_reset() {
6900
		return Jetpack_Options::get_options_for_reset();
6901
	}
6902
6903
	/**
6904
	 * Check if an option of a Jetpack module has been updated.
6905
	 *
6906
	 * If any module option has been updated before Jump Start has been dismissed,
6907
	 * update the 'jumpstart' option so we can hide Jump Start.
6908
	 *
6909
	 * @param string $option_name
6910
	 *
6911
	 * @return bool
6912
	 */
6913
	public static function jumpstart_has_updated_module_option( $option_name = '' ) {
6914
		// Bail if Jump Start has already been dismissed
6915
		if ( 'new_connection' !== Jetpack_Options::get_option( 'jumpstart' ) ) {
6916
			return false;
6917
		}
6918
6919
		$jetpack = Jetpack::init();
6920
6921
		// Manual build of module options
6922
		$option_names = self::get_jetpack_options_for_reset();
6923
6924
		if ( in_array( $option_name, $option_names['wp_options'] ) ) {
6925
			Jetpack_Options::update_option( 'jumpstart', 'jetpack_action_taken' );
6926
6927
			//Jump start is being dismissed send data to MC Stats
6928
			$jetpack->stat( 'jumpstart', 'manual,'.$option_name );
6929
6930
			$jetpack->do_stats( 'server_side' );
6931
		}
6932
6933
	}
6934
6935
	/*
6936
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6937
	 * so we can bring them directly to their site in calypso.
6938
	 *
6939
	 * @param string | url
6940
	 * @return string | url without the guff
6941
	 */
6942
	public static function build_raw_urls( $url ) {
6943
		$strip_http = '/.*?:\/\//i';
6944
		$url = preg_replace( $strip_http, '', $url  );
6945
		$url = str_replace( '/', '::', $url );
6946
		return $url;
6947
	}
6948
6949
	/**
6950
	 * Stores and prints out domains to prefetch for page speed optimization.
6951
	 *
6952
	 * @param mixed $new_urls
6953
	 */
6954
	public static function dns_prefetch( $new_urls = null ) {
6955
		static $prefetch_urls = array();
6956
		if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) {
6957
			echo "\r\n";
6958
			foreach ( $prefetch_urls as $this_prefetch_url ) {
6959
				printf( "<link rel='dns-prefetch' href='%s'/>\r\n", esc_attr( $this_prefetch_url ) );
6960
			}
6961
		} elseif ( ! empty( $new_urls ) ) {
6962
			if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) {
6963
				add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) );
6964
			}
6965
			foreach ( (array) $new_urls as $this_new_url ) {
6966
				$prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) );
6967
			}
6968
			$prefetch_urls = array_unique( $prefetch_urls );
6969
		}
6970
	}
6971
6972
	public function wp_dashboard_setup() {
6973
		if ( self::is_active() ) {
6974
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6975
		}
6976
6977
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6978
			wp_add_dashboard_widget(
6979
				'jetpack_summary_widget',
6980
				esc_html__( 'Site Stats', 'jetpack' ),
6981
				array( __CLASS__, 'dashboard_widget' )
6982
			);
6983
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6984
6985
			// If we're inactive and not in development mode, sort our box to the top.
6986
			if ( ! self::is_active() && ! self::is_development_mode() ) {
6987
				global $wp_meta_boxes;
6988
6989
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6990
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6991
6992
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6993
			}
6994
		}
6995
	}
6996
6997
	/**
6998
	 * @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...
6999
	 * @return mixed
7000
	 */
7001
	function get_user_option_meta_box_order_dashboard( $sorted ) {
7002
		if ( ! is_array( $sorted ) ) {
7003
			return $sorted;
7004
		}
7005
7006
		foreach ( $sorted as $box_context => $ids ) {
7007
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
7008
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
7009
				continue;
7010
			}
7011
7012
			$ids_array = explode( ',', $ids );
7013
			$key = array_search( 'dashboard_stats', $ids_array );
7014
7015
			if ( false !== $key ) {
7016
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
7017
				$ids_array[ $key ] = 'jetpack_summary_widget';
7018
				$sorted[ $box_context ] = implode( ',', $ids_array );
7019
				// We've found it, stop searching, and just return.
7020
				break;
7021
			}
7022
		}
7023
7024
		return $sorted;
7025
	}
7026
7027
	public static function dashboard_widget() {
7028
		/**
7029
		 * Fires when the dashboard is loaded.
7030
		 *
7031
		 * @since 3.4.0
7032
		 */
7033
		do_action( 'jetpack_dashboard_widget' );
7034
	}
7035
7036
	public static function dashboard_widget_footer() {
7037
		?>
7038
		<footer>
7039
7040
		<div class="protect">
7041
			<?php if ( Jetpack::is_module_active( 'protect' ) ) : ?>
7042
				<h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3>
7043
				<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>
7044
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! self::is_development_mode() ) : ?>
7045
				<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' ); ?>">
7046
					<?php esc_html_e( 'Activate Protect', 'jetpack' ); ?>
7047
				</a>
7048
			<?php else : ?>
7049
				<?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?>
7050
			<?php endif; ?>
7051
		</div>
7052
7053
		<div class="akismet">
7054
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
7055
				<h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3>
7056
				<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>
7057
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
7058
				<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">
7059
					<?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?>
7060
				</a>
7061
			<?php else : ?>
7062
				<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>
7063
			<?php endif; ?>
7064
		</div>
7065
7066
		</footer>
7067
		<?php
7068
	}
7069
7070
	/**
7071
	 * Return string containing the Jetpack logo.
7072
	 *
7073
	 * @since 3.9.0
7074
	 *
7075
	 * @return string
7076
	 */
7077
	public static function get_jp_emblem() {
7078
		return '<svg id="jetpack-logo__icon" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 32 32"><path fill="#00BE28" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16c8.8,0,16-7.2,16-16S24.8,0,16,0z M15.2,18.7h-8l8-15.5V18.7z M16.8,28.8 V13.3h8L16.8,28.8z"/></svg>';
7079
	}
7080
7081
	/*
7082
	 * Adds a "blank" column in the user admin table to display indication of user connection.
7083
	 */
7084
	function jetpack_icon_user_connected( $columns ) {
7085
		$columns['user_jetpack'] = '';
7086
		return $columns;
7087
	}
7088
7089
	/*
7090
	 * Show Jetpack icon if the user is linked.
7091
	 */
7092
	function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
7093
		if ( 'user_jetpack' == $col && Jetpack::is_user_connected( $user_id ) ) {
7094
			$emblem_html = sprintf(
7095
				'<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
7096
				esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
7097
				Jetpack::get_jp_emblem()
7098
			);
7099
			return $emblem_html;
7100
		}
7101
7102
		return $val;
7103
	}
7104
7105
	/*
7106
	 * Style the Jetpack user column
7107
	 */
7108
	function jetpack_user_col_style() {
7109
		global $current_screen;
7110
		if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) { ?>
7111
			<style>
7112
				.fixed .column-user_jetpack {
7113
					width: 21px;
7114
				}
7115
				.jp-emblem-user-admin svg {
7116
					width: 20px;
7117
					height: 20px;
7118
				}
7119
				.jp-emblem-user-admin path {
7120
					fill: #00BE28;
7121
				}
7122
			</style>
7123
		<?php }
7124
	}
7125
7126
	/**
7127
	 * Checks if Akismet is active and working.
7128
	 *
7129
	 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
7130
	 * that implied usage of methods present since more recent version.
7131
	 * See https://github.com/Automattic/jetpack/pull/9585
7132
	 *
7133
	 * @since  5.1.0
7134
	 *
7135
	 * @return bool True = Akismet available. False = Aksimet not available.
7136
	 */
7137
	public static function is_akismet_active() {
7138
		static $status = null;
7139
7140
		if ( ! is_null( $status ) ) {
7141
			return $status;
7142
		}
7143
7144
		// Check if a modern version of Akismet is active.
7145
		if ( ! method_exists( 'Akismet', 'http_post' ) ) {
7146
			$status = false;
7147
			return $status;
7148
		}
7149
7150
		// Make sure there is a key known to Akismet at all before verifying key.
7151
		$akismet_key = Akismet::get_api_key();
7152
		if ( ! $akismet_key ) {
7153
			$status = false;
7154
			return $status;
7155
		}
7156
7157
		// Possible values: valid, invalid, failure via Akismet. false if no status is cached.
7158
		$akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
7159
7160
		// Do not used the cache result in wp-admin or REST API requests if the key isn't valid, in case someone is actively renewing, etc.
7161
		$recheck = ( ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state ) ? true : false;
7162
		// We cache the result of the Akismet key verification for ten minutes.
7163
		if ( ! $akismet_key_state || $recheck ) {
7164
			$akismet_key_state = Akismet::verify_key( $akismet_key );
7165
			set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
7166
		}
7167
7168
		$status = ( 'valid' === $akismet_key_state );
7169
7170
		return $status;
7171
	}
7172
7173
	/**
7174
	 * Checks if one or more function names is in debug_backtrace
7175
	 *
7176
	 * @param $names Mixed string name of function or array of string names of functions
7177
	 *
7178
	 * @return bool
7179
	 */
7180
	public static function is_function_in_backtrace( $names ) {
7181
		$backtrace = debug_backtrace( false ); // phpcs:ignore PHPCompatibility.PHP.NewFunctionParameters.debug_backtrace_optionsFound
7182
		if ( ! is_array( $names ) ) {
7183
			$names = array( $names );
7184
		}
7185
		$names_as_keys = array_flip( $names );
7186
7187
		//Do check in constant O(1) time for PHP5.5+
7188
		if ( function_exists( 'array_column' ) ) {
7189
			$backtrace_functions = array_column( $backtrace, 'function' ); // phpcs:ignore PHPCompatibility.PHP.NewFunctions.array_columnFound
7190
			$backtrace_functions_as_keys = array_flip( $backtrace_functions );
7191
			$intersection = array_intersect_key( $backtrace_functions_as_keys, $names_as_keys );
7192
			return ! empty ( $intersection );
7193
		}
7194
7195
		//Do check in linear O(n) time for < PHP5.5 ( using isset at least prevents O(n^2) )
7196
		foreach ( $backtrace as $call ) {
7197
			if ( isset( $names_as_keys[ $call['function'] ] ) ) {
7198
				return true;
7199
			}
7200
		}
7201
		return false;
7202
	}
7203
7204
	/**
7205
	 * Given a minified path, and a non-minified path, will return
7206
	 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
7207
	 *
7208
	 * Both `$min_base` and `$non_min_base` are expected to be relative to the
7209
	 * root Jetpack directory.
7210
	 *
7211
	 * @since 5.6.0
7212
	 *
7213
	 * @param string $min_path
7214
	 * @param string $non_min_path
7215
	 * @return string The URL to the file
7216
	 */
7217
	public static function get_file_url_for_environment( $min_path, $non_min_path ) {
7218
		$path = ( Jetpack_Constants::is_defined( 'SCRIPT_DEBUG' ) && Jetpack_Constants::get_constant( 'SCRIPT_DEBUG' ) )
7219
			? $non_min_path
7220
			: $min_path;
7221
7222
		return plugins_url( $path, JETPACK__PLUGIN_FILE );
7223
	}
7224
7225
	/**
7226
	 * Checks for whether Jetpack Rewind is enabled.
7227
	 * Will return true if the state of Rewind is anything except "unavailable".
7228
	 * @return bool|int|mixed
7229
	 */
7230
	public static function is_rewind_enabled() {
7231
		if ( ! Jetpack::is_active() ) {
7232
			return false;
7233
		}
7234
7235
		$rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7236
		if ( false === $rewind_enabled ) {
7237
			jetpack_require_lib( 'class.core-rest-api-endpoints' );
7238
			$rewind_data = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7239
			$rewind_enabled = ( ! is_wp_error( $rewind_data )
7240
				&& ! empty( $rewind_data['state'] )
7241
				&& 'active' === $rewind_data['state'] )
7242
				? 1
7243
				: 0;
7244
7245
			set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7246
		}
7247
		return $rewind_enabled;
7248
	}
7249
7250
	/**
7251
	 * Checks whether or not TOS has been agreed upon.
7252
	 * Will return true if a user has clicked to register, or is already connected.
7253
	 */
7254
	public static function jetpack_tos_agreed() {
7255
		return Jetpack_Options::get_option( 'tos_agreed' ) || Jetpack::is_active();
7256
	}
7257
7258
	/**
7259
	 * Handles activating default modules as well general cleanup for the new connection.
7260
	 *
7261
	 * @param boolean $activate_sso                 Whether to activate the SSO module when activating default modules.
7262
	 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7263
	 * @param boolean $send_state_messages          Whether to send state messages.
7264
	 * @return void
7265
	 */
7266
	public static function handle_post_authorization_actions(
7267
		$activate_sso = false,
7268
		$redirect_on_activation_error = false,
7269
		$send_state_messages = true
7270
	) {
7271
		$other_modules = $activate_sso
7272
			? array( 'sso' )
7273
			: array();
7274
7275
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
7276
			Jetpack::delete_active_modules();
7277
7278
			Jetpack::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, $send_state_messages );
0 ignored issues
show
Documentation introduced by
999 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
7279
		} else {
7280
			Jetpack::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages );
7281
		}
7282
7283
		// Since this is a fresh connection, be sure to clear out IDC options
7284
		Jetpack_IDC::clear_all_idc_options();
7285
		Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' );
7286
7287
		// Start nonce cleaner
7288
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
7289
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
7290
7291
		if ( $send_state_messages ) {
7292
			Jetpack::state( 'message', 'authorized' );
7293
		}
7294
	}
7295
7296
	/**
7297
	 * Returns a boolean for whether backups UI should be displayed or not.
7298
	 *
7299
	 * @return bool Should backups UI be displayed?
7300
	 */
7301
	public static function show_backups_ui() {
7302
		/**
7303
		 * Whether UI for backups should be displayed.
7304
		 *
7305
		 * @since 6.5.0
7306
		 *
7307
		 * @param bool $show_backups Should UI for backups be displayed? True by default.
7308
		 */
7309
		return Jetpack::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7310
	}
7311
}
7312