PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.7.2
Jetpack – WP Security, Backup, Speed, & Growth v13.7.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / class.jetpack-admin.php
jetpack Last commit date
3rd-party 2 years ago _inc 2 years ago css 2 years ago extensions 2 years ago images 2 years ago jetpack_vendor 21 hours ago json-endpoints 2 years ago modules 2 years ago sal 2 years ago src 2 years ago vendor 2 years ago views 2 years ago CHANGELOG.md 2 years ago LICENSE.txt 5 years ago SECURITY.md 2 years ago class-jetpack-connection-status.php 2 years ago class-jetpack-gallery-settings.php 3 years ago class-jetpack-pre-connection-jitms.php 2 years ago class-jetpack-stats-dashboard-widget.php 2 years ago class-jetpack-xmlrpc-methods.php 2 years ago class.frame-nonce-preview.php 4 years ago class.jetpack-admin.php 2 years ago class.jetpack-affiliate.php 2 years ago class.jetpack-autoupdate.php 2 years ago class.jetpack-bbpress-json-api.compat.php 2 years ago class.jetpack-cli.php 2 years ago class.jetpack-client-server.php 2 years ago class.jetpack-gutenberg.php 2 years ago class.jetpack-heartbeat.php 2 years ago class.jetpack-modules-list-table.php 2 years ago class.jetpack-network-sites-list-table.php 2 years ago class.jetpack-network.php 2 years ago class.jetpack-plan.php 3 years ago class.jetpack-post-images.php 2 years ago class.jetpack-twitter-cards.php 2 years ago class.jetpack-user-agent.php 2 years ago class.jetpack.php 2 years ago class.json-api-endpoints.php 2 years ago class.json-api.php 2 years ago class.photon.php 3 years ago composer.json 2 years ago enhanced-open-graph.php 3 years ago functions.compat.php 2 years ago functions.cookies.php 2 years ago functions.global.php 2 years ago functions.is-mobile.php 2 years ago functions.opengraph.php 2 years ago functions.photon.php 2 years ago global.d.ts 2 years ago jetpack.php 21 hours ago json-api-config.php 3 years ago json-endpoints.php 2 years ago load-jetpack.php 2 years ago locales.php 4 years ago readme.txt 21 hours ago uninstall.php 2 years ago wpml-config.xml 4 years ago
class.jetpack-admin.php
615 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Build the Jetpack admin menu as a whole.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Admin_UI\Admin_Menu;
9 use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
10 use Automattic\Jetpack\Partner_Coupon as Jetpack_Partner_Coupon;
11 use Automattic\Jetpack\Status;
12 use Automattic\Jetpack\Status\Host;
13
14 /**
15 * Build the Jetpack admin menu as a whole.
16 */
17 class Jetpack_Admin {
18
19 /**
20 * Static instance.
21 *
22 * @var Jetpack_Admin
23 */
24 private static $instance = null;
25
26 /**
27 * Initialize and fetch the static instance.
28 *
29 * @return self
30 */
31 public static function init() {
32 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
33 if ( isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] ) {
34 add_filter( 'nocache_headers', array( 'Jetpack_Admin', 'add_no_store_header' ), 100 );
35 }
36
37 if ( self::$instance === null ) {
38 self::$instance = new Jetpack_Admin();
39 }
40 return self::$instance;
41 }
42
43 /**
44 * Filter callback to add `no-store` to the `Cache-Control` header.
45 *
46 * @param array $headers Headers array.
47 * @return array Modified headers array.
48 */
49 public static function add_no_store_header( $headers ) {
50 $headers['Cache-Control'] .= ', no-store';
51 return $headers;
52 }
53
54 /** Constructor. */
55 private function __construct() {
56 require_once JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class.jetpack-react-page.php';
57 $jetpack_react = new Jetpack_React_Page();
58
59 require_once JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class.jetpack-settings-page.php';
60 $fallback_page = new Jetpack_Settings_Page();
61
62 require_once JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class-jetpack-about-page.php';
63 $jetpack_about = new Jetpack_About_Page();
64
65 add_action( 'admin_init', array( $jetpack_react, 'react_redirects' ), 0 );
66 add_action( 'admin_menu', array( $jetpack_react, 'add_actions' ), 998 );
67 add_action( 'jetpack_admin_menu', array( $jetpack_react, 'jetpack_add_dashboard_sub_nav_item' ) );
68 add_action( 'jetpack_admin_menu', array( $jetpack_react, 'jetpack_add_settings_sub_nav_item' ) );
69 add_action( 'jetpack_admin_menu', array( $this, 'admin_menu_debugger' ) );
70 add_action( 'jetpack_admin_menu', array( $fallback_page, 'add_actions' ) );
71 add_action( 'jetpack_admin_menu', array( $jetpack_about, 'add_actions' ) );
72
73 // Add redirect to current page for activation/deactivation of modules.
74 add_action( 'jetpack_pre_activate_module', array( $this, 'fix_redirect' ), 10, 2 );
75 add_action( 'jetpack_pre_deactivate_module', array( $this, 'fix_redirect' ), 10, 2 );
76
77 // Add module bulk actions handler.
78 add_action( 'jetpack_unrecognized_action', array( $this, 'handle_unrecognized_action' ) );
79
80 if ( class_exists( 'Akismet_Admin' ) ) {
81 // If the site has Jetpack Anti-spam, change the Akismet menu label and logo accordingly.
82 $site_products = array_column( Jetpack_Plan::get_products(), 'product_slug' );
83 $has_anti_spam_product = count( array_intersect( array( 'jetpack_anti_spam', 'jetpack_anti_spam_monthly' ), $site_products ) ) > 0;
84
85 if ( Jetpack_Plan::supports( 'akismet' ) || Jetpack_Plan::supports( 'antispam' ) || $has_anti_spam_product ) {
86 // Prevent Akismet from adding a menu item.
87 add_action(
88 'admin_menu',
89 function () {
90 remove_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 );
91 },
92 4
93 );
94
95 // Add an Anti-spam menu item for Jetpack. This is handled automatically by the Admin_Menu as long as it has been initialized.
96 Admin_Menu::init();
97 add_action( 'admin_enqueue_scripts', array( $this, 'akismet_logo_replacement_styles' ) );
98 }
99 }
100
101 // Ensure an Additional CSS menu item is added to the Appearance menu whenever Jetpack is connected.
102 add_action( 'admin_menu', array( $this, 'additional_css_menu' ) );
103
104 add_filter( 'jetpack_display_jitms_on_screen', array( $this, 'should_display_jitms_on_screen' ), 10, 2 );
105
106 // Register Jetpack partner coupon hooks.
107 Jetpack_Partner_Coupon::register_coupon_admin_hooks( 'jetpack', Jetpack::admin_url() );
108 }
109
110 /**
111 * Generate styles to replace Akismet logo for the Jetpack Akismet Anti-spam logo.
112 Without this, we would have to change the logo from Akismet codebase and we want to avoid that.
113 */
114 public function akismet_logo_replacement_styles() {
115 $logo_url = esc_url( plugins_url( 'images/products/logo-anti-spam.svg', JETPACK__PLUGIN_FILE ) );
116 $style = ".akismet-masthead__logo-container { background: url({$logo_url}) no-repeat; min-height: 42px; margin: 20px 0; padding: 0 !important; } .akismet-masthead__logo { display: none; }";
117 $style .= '@media screen and (max-width: 782px) { .akismet-masthead__logo-container { margin-left: 4px; } }';
118 wp_add_inline_style( 'admin-bar', $style );
119 }
120
121 /**
122 * Handle our Additional CSS menu item and legacy page declaration.
123 *
124 * @since 11.0 . Prior to that, this function was located in custom-css-4.7.php (now custom-css.php).
125 */
126 public static function additional_css_menu() {
127 /*
128 * Custom CSS for the Customizer is deprecated for block themes as of WP 6.1, so we only expose it with a menu
129 * if the site already has existing CSS code.
130 */
131 if ( wp_is_block_theme() ) {
132 $styles = wp_get_custom_css();
133 if ( ! $styles ) {
134 return;
135 }
136 }
137
138 // If the site is a WoA site and the custom-css feature is not available, return.
139 // See https://github.com/Automattic/jetpack/pull/19965 for more on how this menu item is dealt with on WoA sites.
140 if ( ( new Host() )->is_woa_site() && ! ( in_array( 'custom-css', Jetpack::get_available_modules(), true ) ) ) {
141 return;
142 } elseif (
143 class_exists( 'Jetpack' ) && (
144 Jetpack::is_module_active( 'custom-css' ) || // If the Custom CSS module is enabled, add the Additional CSS menu item and link to the Customizer.
145 ( wp_is_block_theme() && ! empty( wp_get_custom_css() ) ) // Do the same if the theme is block-based but has existing custom CSS.
146 )
147 ) {
148 // Add in our legacy page to support old bookmarks and such.
149 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'customizer_redirect' ) );
150
151 // Add in our new page slug that will redirect to the customizer.
152 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-customizer-redirect', array( __CLASS__, 'customizer_redirect' ) );
153 add_action( "load-{$hook}", array( __CLASS__, 'customizer_redirect' ) );
154 } elseif ( class_exists( 'Jetpack' ) && Jetpack::is_connection_ready() ) { // Link to the Jetpack Settings > Writing page, highlighting the Custom CSS setting.
155 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'theme_enhancements_redirect' ) );
156
157 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-theme-enhancements-redirect', array( __CLASS__, 'theme_enhancements_redirect' ) );
158 add_action( "load-{$hook}", array( __CLASS__, 'theme_enhancements_redirect' ) );
159 }
160 }
161
162 /**
163 * Handle the redirect for the customizer. This is necessary because
164 * we can't directly add customizer links to the admin menu.
165 *
166 * @since 11.0 . Prior to that, this function was located in custom-css-4.7.php (now custom-css.php).
167 *
168 * There is a core patch in trac that would make this unnecessary.
169 *
170 * @link https://core.trac.wordpress.org/ticket/39050
171 *
172 * @return never
173 */
174 public static function customizer_redirect() {
175 wp_safe_redirect(
176 self::customizer_link(
177 array(
178 'return_url' => wp_get_referer(),
179 )
180 )
181 );
182 exit;
183 }
184
185 /**
186 * Handle the Additional CSS redirect to the Jetpack settings Theme Enhancements section.
187 *
188 * @since 11.0
189 *
190 * @return never
191 */
192 public static function theme_enhancements_redirect() {
193 wp_safe_redirect(
194 'admin.php?page=jetpack#/writing?term=custom-css'
195 );
196 exit;
197 }
198
199 /**
200 * Build the URL to deep link to the Customizer.
201 *
202 * You can modify the return url via $args.
203 *
204 * @since 11.0 in this file. This method is also located in custom-css-4.7.php to cover legacy scenarios.
205 *
206 * @param array $args Array of parameters.
207 * @return string
208 */
209 public static function customizer_link( $args = array() ) {
210 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
211 $args = wp_parse_args(
212 $args,
213 array(
214 'return_url' => rawurlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
215 )
216 );
217 }
218
219 return add_query_arg(
220 array(
221 array(
222 'autofocus' => array(
223 'section' => 'custom_css',
224 ),
225 ),
226 'return' => $args['return_url'],
227 ),
228 admin_url( 'customize.php' )
229 );
230 }
231
232 /**
233 * Sort callback to put modules with `requires_connection` last.
234 *
235 * @param array $module1 Module data.
236 * @param array $module2 Module data.
237 * @return int Indicating the relative ordering of module1 and module2.
238 */
239 public static function sort_requires_connection_last( $module1, $module2 ) {
240 return ( (bool) $module1['requires_connection'] ) <=> ( (bool) $module2['requires_connection'] );
241 }
242
243 /**
244 * Produce JS understandable objects of modules containing information for
245 * presentation like description, name, configuration url, etc.
246 */
247 public function get_modules() {
248 include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
249 $available_modules = Jetpack::get_available_modules();
250 $active_modules = Jetpack::get_active_modules();
251 $modules = array();
252 $jetpack_active = Jetpack::is_connection_ready() || ( new Status() )->is_offline_mode();
253 $overrides = Jetpack_Modules_Overrides::instance();
254 foreach ( $available_modules as $module ) {
255 $module_array = Jetpack::get_module( $module );
256 if ( $module_array ) {
257 /**
258 * Filters each module's short description.
259 *
260 * @since 3.0.0
261 *
262 * @param string $module_array['description'] Module description.
263 * @param string $module Module slug.
264 */
265 $short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
266 // Fix: correct multibyte strings truncate with checking for mbstring extension.
267 $short_desc_trunc = ( function_exists( 'mb_strlen' ) )
268 ? ( ( mb_strlen( $short_desc ) > 143 )
269 ? mb_substr( $short_desc, 0, 140 ) . '...'
270 : $short_desc )
271 : ( ( strlen( $short_desc ) > 143 )
272 ? substr( $short_desc, 0, 140 ) . '...'
273 : $short_desc );
274
275 $module_array['module'] = $module;
276
277 $is_available = self::is_module_available( $module_array );
278
279 $module_array['activated'] = ( $jetpack_active ? in_array( $module, $active_modules, true ) : false );
280 $module_array['deactivate_nonce'] = wp_create_nonce( 'jetpack_deactivate-' . $module );
281 $module_array['activate_nonce'] = wp_create_nonce( 'jetpack_activate-' . $module );
282 $module_array['available'] = $is_available;
283 $module_array['unavailable_reason'] = $is_available ? false : self::get_module_unavailable_reason( $module_array );
284 $module_array['short_description'] = $short_desc_trunc;
285 $module_array['configure_url'] = Jetpack::module_configuration_url( $module );
286 $module_array['override'] = $overrides->get_module_override( $module );
287 $module_array['disabled'] = $is_available ? '' : 'disabled="disabled"';
288
289 ob_start();
290 /**
291 * Allow the display of a "Learn More" button.
292 * The dynamic part of the action, $module, is the module slug.
293 *
294 * @since 3.0.0
295 */
296 do_action( 'jetpack_learn_more_button_' . $module );
297 $module_array['learn_more_button'] = ob_get_clean();
298
299 ob_start();
300 /**
301 * Allow the display of information text when Jetpack is connected to WordPress.com.
302 * The dynamic part of the action, $module, is the module slug.
303 *
304 * @since 3.0.0
305 */
306 do_action( 'jetpack_module_more_info_' . $module );
307
308 /**
309 * Filter the long description of a module.
310 *
311 * @since 3.5.0
312 *
313 * @param string ob_get_clean() The module long description.
314 * @param string $module The module name.
315 */
316 $module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
317
318 ob_start();
319 /**
320 * Filter the search terms for a module
321 *
322 * Search terms are typically added to the module headers, under "Additional Search Queries".
323 *
324 * Use syntax:
325 * function jetpack_$module_search_terms( $terms ) {
326 * $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
327 * return $terms;
328 * }
329 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
330 *
331 * @since 3.5.0
332 *
333 * @param string The search terms (comma separated).
334 */
335 echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
336 $module_array['search_terms'] = ob_get_clean();
337
338 $module_array['configurable'] = false;
339 if (
340 current_user_can( 'manage_options' ) &&
341 /**
342 * Allow the display of a configuration link in the Jetpack Settings screen.
343 *
344 * @since 3.0.0
345 *
346 * @param string $module Module name.
347 * @param bool false Should the Configure module link be displayed? Default to false.
348 */
349 apply_filters( 'jetpack_module_configurable_' . $module, false )
350 ) {
351 $module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
352 }
353
354 $modules[ $module ] = $module_array;
355 }
356 }
357
358 uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
359
360 if ( ! Jetpack::is_connection_ready() ) {
361 uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
362 }
363
364 return $modules;
365 }
366
367 /**
368 * Check if a module is available.
369 *
370 * @param array $module Module data.
371 */
372 public static function is_module_available( $module ) {
373 if ( ! is_array( $module ) || empty( $module ) ) {
374 return false;
375 }
376
377 /**
378 * We never want to show VaultPress as activatable through Jetpack.
379 */
380 if ( 'vaultpress' === $module['module'] ) {
381 return false;
382 }
383
384 /*
385 * WooCommerce Analytics should only be available
386 * when running WooCommerce 3+
387 */
388 if (
389 'woocommerce-analytics' === $module['module']
390 && (
391 ! class_exists( 'WooCommerce' )
392 || version_compare( WC_VERSION, '3.0', '<' )
393 )
394 ) {
395 return false;
396 }
397
398 /*
399 * In Offline mode, modules that require a site or user
400 * level connection should be unavailable.
401 */
402 if ( ( new Status() )->is_offline_mode() ) {
403 return ! ( $module['requires_connection'] || $module['requires_user_connection'] );
404 }
405
406 /*
407 * Jetpack not connected.
408 */
409 if ( ! Jetpack::is_connection_ready() ) {
410 return false;
411 }
412
413 /*
414 * Jetpack connected at a site level only. Make sure to make
415 * modules that require a user connection unavailable.
416 */
417 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
418 return false;
419 }
420
421 return Jetpack_Plan::supports( $module['module'] );
422 }
423
424 /**
425 * Returns why a module is unavailable.
426 *
427 * @param array $module The module.
428 * @return string|false A string stating why the module is not available or false if the module is available.
429 */
430 public static function get_module_unavailable_reason( $module ) {
431 if ( ! is_array( $module ) || empty( $module ) ) {
432 return false;
433 }
434
435 if ( self::is_module_available( $module ) ) {
436 return false;
437 }
438
439 /**
440 * We never want to show VaultPress as activatable through Jetpack so return an empty string.
441 */
442 if ( 'vaultpress' === $module['module'] ) {
443 return '';
444 }
445
446 /*
447 * WooCommerce Analytics should only be available
448 * when running WooCommerce 3+
449 */
450 if (
451 'woocommerce-analytics' === $module['module']
452 && (
453 ! class_exists( 'WooCommerce' )
454 || version_compare( WC_VERSION, '3.0', '<' )
455 )
456 ) {
457 return __( 'Requires WooCommerce 3+ plugin', 'jetpack' );
458 }
459
460 /*
461 * In Offline mode, modules that require a site or user
462 * level connection should be unavailable.
463 */
464 if ( ( new Status() )->is_offline_mode() ) {
465 if ( $module['requires_connection'] || $module['requires_user_connection'] ) {
466 return __( 'Offline mode', 'jetpack' );
467 }
468 }
469
470 /*
471 * Jetpack not connected.
472 */
473 if ( ! Jetpack::is_connection_ready() ) {
474 return __( 'Jetpack is not connected', 'jetpack' );
475 }
476
477 /*
478 * Jetpack connected at a site level only and module requires a user connection.
479 */
480 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
481 return __( 'Requires a connected WordPress.com account', 'jetpack' );
482 }
483
484 /*
485 * Plan restrictions.
486 */
487 if ( ! Jetpack_Plan::supports( $module['module'] ) ) {
488 return __( 'Not supported by current plan', 'jetpack' );
489 }
490
491 return '';
492 }
493
494 /**
495 * Handle an unrecognized action.
496 *
497 * @param string $action Action.
498 */
499 public function handle_unrecognized_action( $action ) {
500 switch ( $action ) {
501 case 'bulk-activate':
502 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
503 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
504 break;
505 }
506
507 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
508 foreach ( $modules as $module ) {
509 Jetpack::log( 'activate', $module );
510 Jetpack::activate_module( $module, false );
511 }
512 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
513 wp_safe_redirect( wp_get_referer() );
514 exit;
515 case 'bulk-deactivate':
516 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
517 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
518 break;
519 }
520
521 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
522 foreach ( $modules as $module ) {
523 Jetpack::log( 'deactivate', $module );
524 Jetpack::deactivate_module( $module );
525 Jetpack::state( 'message', 'module_deactivated' );
526 }
527 Jetpack::state( 'module', $modules );
528 wp_safe_redirect( wp_get_referer() );
529 exit;
530 default:
531 return;
532 }
533 }
534
535 /**
536 * Fix redirect.
537 *
538 * Apparently we redirect to the referrer instead of whatever WordPress
539 * wants to redirect to when activating and deactivating modules.
540 *
541 * @param string $module Module slug.
542 * @param bool $redirect Should we exit after the module has been activated. Default to true.
543 */
544 public function fix_redirect( $module, $redirect = true ) {
545 if ( ! $redirect ) {
546 return;
547 }
548 if ( wp_get_referer() ) {
549 add_filter( 'wp_redirect', 'wp_get_referer' );
550 }
551 }
552
553 /**
554 * Add debugger admin menu.
555 */
556 public function admin_menu_debugger() {
557 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
558 Jetpack_Debugger::disconnect_and_redirect();
559 $debugger_hook = add_submenu_page(
560 '',
561 __( 'Debugging Center', 'jetpack' ),
562 '',
563 'manage_options',
564 'jetpack-debugger',
565 array( $this, 'wrap_debugger_page' )
566 );
567 add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
568 }
569
570 /**
571 * Wrap debugger page.
572 */
573 public function wrap_debugger_page() {
574 nocache_headers();
575 if ( ! current_user_can( 'manage_options' ) ) {
576 die( '-1' );
577 }
578 Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ), array( 'is-wide' => true ) );
579 }
580
581 /**
582 * Display debugger page.
583 */
584 public function debugger_page() {
585 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
586 Jetpack_Debugger::jetpack_debug_display_handler();
587 }
588
589 /**
590 * Determines if JITMs should display on a particular screen.
591 *
592 * @param bool $value The default value of the filter.
593 * @param string $screen_id The ID of the screen being tested for JITM display.
594 *
595 * @return bool True if JITMs should display, false otherwise.
596 */
597 public function should_display_jitms_on_screen( $value, $screen_id ) {
598 // Disable all JITMs on these pages.
599 if (
600 in_array(
601 $screen_id,
602 array(
603 'jetpack_page_akismet-key-config',
604 'admin_page_jetpack_modules',
605 ),
606 true
607 ) ) {
608 return false;
609 }
610
611 return $value;
612 }
613 }
614 Jetpack_Admin::init();
615