PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.8.3
Jetpack – WP Security, Backup, Speed, & Growth v12.8.3
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 3 years ago jetpack_vendor 2 days ago json-endpoints 2 years ago modules 1 year ago sal 2 years ago src 3 years ago vendor 2 years ago views 3 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-connection-widget.php 3 years ago class-jetpack-gallery-settings.php 3 years ago class-jetpack-pre-connection-jitms.php 4 years ago class-jetpack-recommendations-banner.php 2 years ago class-jetpack-stats-dashboard-widget.php 3 years ago class-jetpack-wizard-banner.php 5 years ago class-jetpack-xmlrpc-methods.php 3 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-boost-modules.php 3 years ago class.jetpack-cli.php 3 years ago class.jetpack-client-server.php 4 years ago class.jetpack-connection-banner.php 3 years ago class.jetpack-data.php 2 years ago class.jetpack-gutenberg.php 2 years ago class.jetpack-heartbeat.php 2 years ago class.jetpack-idc.php 5 years ago class.jetpack-modules-list-table.php 2 years ago class.jetpack-network-sites-list-table.php 3 years ago class.jetpack-network.php 3 years ago class.jetpack-plan.php 3 years ago class.jetpack-post-images.php 2 years ago class.jetpack-twitter-cards.php 3 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 3 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 jetpack.php 2 days ago json-api-config.php 3 years ago json-endpoints.php 2 years ago load-jetpack.php 3 years ago locales.php 4 years ago readme.txt 2 days ago uninstall.php 2 years ago wpml-config.xml 4 years ago
class.jetpack-admin.php
630 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( '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 ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'custom-css' ) ) { // If the Custom CSS module is enabled, add the Additional CSS menu item and link to the Customizer.
143 // Add in our legacy page to support old bookmarks and such.
144 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'customizer_redirect' ) );
145
146 // Add in our new page slug that will redirect to the customizer.
147 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-customizer-redirect', array( __CLASS__, 'customizer_redirect' ) );
148 add_action( "load-{$hook}", array( __CLASS__, 'customizer_redirect' ) );
149 } elseif ( class_exists( 'Jetpack' ) && Jetpack::is_connection_ready() ) { // Link to the Jetpack Settings > Writing page, highlighting the Custom CSS setting.
150 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'theme_enhancements_redirect' ) );
151
152 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-theme-enhancements-redirect', array( __CLASS__, 'theme_enhancements_redirect' ) );
153 add_action( "load-{$hook}", array( __CLASS__, 'theme_enhancements_redirect' ) );
154 }
155 }
156
157 /**
158 * Handle the redirect for the customizer. This is necessary because
159 * we can't directly add customizer links to the admin menu.
160 *
161 * @since 11.0 . Prior to that, this function was located in custom-css-4.7.php (now custom-css.php).
162 *
163 * There is a core patch in trac that would make this unnecessary.
164 *
165 * @link https://core.trac.wordpress.org/ticket/39050
166 */
167 public static function customizer_redirect() {
168 wp_safe_redirect(
169 self::customizer_link(
170 array(
171 'return_url' => wp_get_referer(),
172 )
173 )
174 );
175 exit;
176 }
177
178 /**
179 * Handle the Additional CSS redirect to the Jetpack settings Theme Enhancements section.
180 *
181 * @since 11.0
182 */
183 public static function theme_enhancements_redirect() {
184 wp_safe_redirect(
185 'admin.php?page=jetpack#/writing?term=custom-css'
186 );
187 exit;
188 }
189
190 /**
191 * Build the URL to deep link to the Customizer.
192 *
193 * You can modify the return url via $args.
194 *
195 * @since 11.0 in this file. This method is also located in custom-css-4.7.php to cover legacy scenarios.
196 *
197 * @param array $args Array of parameters.
198 * @return string
199 */
200 public static function customizer_link( $args = array() ) {
201 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
202 $args = wp_parse_args(
203 $args,
204 array(
205 'return_url' => rawurlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
206 )
207 );
208 }
209
210 return add_query_arg(
211 array(
212 array(
213 'autofocus' => array(
214 'section' => 'custom_css',
215 ),
216 ),
217 'return' => $args['return_url'],
218 ),
219 admin_url( 'customize.php' )
220 );
221 }
222
223 /**
224 * Sort callback to put modules with `requires_connection` last.
225 *
226 * @param array $module1 Module data.
227 * @param array $module2 Module data.
228 * @return int Indicating the relative ordering of module1 and module2.
229 */
230 public static function sort_requires_connection_last( $module1, $module2 ) {
231 if ( (bool) $module1['requires_connection'] === (bool) $module2['requires_connection'] ) {
232 return 0;
233 } elseif ( $module1['requires_connection'] ) {
234 return 1;
235 } elseif ( $module2['requires_connection'] ) {
236 return -1;
237 }
238
239 return 0;
240 }
241
242 /**
243 * Produce JS understandable objects of modules containing information for
244 * presentation like description, name, configuration url, etc.
245 */
246 public function get_modules() {
247 include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
248 $available_modules = Jetpack::get_available_modules();
249 $active_modules = Jetpack::get_active_modules();
250 $modules = array();
251 $jetpack_active = Jetpack::is_connection_ready() || ( new Status() )->is_offline_mode();
252 $overrides = Jetpack_Modules_Overrides::instance();
253 foreach ( $available_modules as $module ) {
254 $module_array = Jetpack::get_module( $module );
255 if ( $module_array ) {
256 /**
257 * Filters each module's short description.
258 *
259 * @since 3.0.0
260 *
261 * @param string $module_array['description'] Module description.
262 * @param string $module Module slug.
263 */
264 $short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
265 // Fix: correct multibyte strings truncate with checking for mbstring extension.
266 $short_desc_trunc = ( function_exists( 'mb_strlen' ) )
267 ? ( ( mb_strlen( $short_desc ) > 143 )
268 ? mb_substr( $short_desc, 0, 140 ) . '...'
269 : $short_desc )
270 : ( ( strlen( $short_desc ) > 143 )
271 ? substr( $short_desc, 0, 140 ) . '...'
272 : $short_desc );
273
274 $module_array['module'] = $module;
275
276 $is_available = self::is_module_available( $module_array );
277
278 $module_array['activated'] = ( $jetpack_active ? in_array( $module, $active_modules, true ) : false );
279 $module_array['deactivate_nonce'] = wp_create_nonce( 'jetpack_deactivate-' . $module );
280 $module_array['activate_nonce'] = wp_create_nonce( 'jetpack_activate-' . $module );
281 $module_array['available'] = $is_available;
282 $module_array['unavailable_reason'] = $is_available ? false : self::get_module_unavailable_reason( $module_array );
283 $module_array['short_description'] = $short_desc_trunc;
284 $module_array['configure_url'] = Jetpack::module_configuration_url( $module );
285 $module_array['override'] = $overrides->get_module_override( $module );
286 $module_array['disabled'] = $is_available ? '' : 'disabled="disabled"';
287
288 ob_start();
289 /**
290 * Allow the display of a "Learn More" button.
291 * The dynamic part of the action, $module, is the module slug.
292 *
293 * @since 3.0.0
294 */
295 do_action( 'jetpack_learn_more_button_' . $module );
296 $module_array['learn_more_button'] = ob_get_clean();
297
298 ob_start();
299 /**
300 * Allow the display of information text when Jetpack is connected to WordPress.com.
301 * The dynamic part of the action, $module, is the module slug.
302 *
303 * @since 3.0.0
304 */
305 do_action( 'jetpack_module_more_info_' . $module );
306
307 /**
308 * Filter the long description of a module.
309 *
310 * @since 3.5.0
311 *
312 * @param string ob_get_clean() The module long description.
313 * @param string $module The module name.
314 */
315 $module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
316
317 ob_start();
318 /**
319 * Filter the search terms for a module
320 *
321 * Search terms are typically added to the module headers, under "Additional Search Queries".
322 *
323 * Use syntax:
324 * function jetpack_$module_search_terms( $terms ) {
325 * $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
326 * return $terms;
327 * }
328 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
329 *
330 * @since 3.5.0
331 *
332 * @param string The search terms (comma separated).
333 */
334 echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
335 $module_array['search_terms'] = ob_get_clean();
336
337 $module_array['configurable'] = false;
338 if (
339 current_user_can( 'manage_options' ) &&
340 /**
341 * Allow the display of a configuration link in the Jetpack Settings screen.
342 *
343 * @since 3.0.0
344 *
345 * @param string $module Module name.
346 * @param bool false Should the Configure module link be displayed? Default to false.
347 */
348 apply_filters( 'jetpack_module_configurable_' . $module, false )
349 ) {
350 $module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
351 }
352
353 $modules[ $module ] = $module_array;
354 }
355 }
356
357 uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
358
359 if ( ! Jetpack::is_connection_ready() ) {
360 uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
361 }
362
363 return $modules;
364 }
365
366 /**
367 * Check if a module is available.
368 *
369 * @param array $module Module data.
370 */
371 public static function is_module_available( $module ) {
372 if ( ! is_array( $module ) || empty( $module ) ) {
373 return false;
374 }
375
376 /**
377 * We never want to show VaultPress as activatable through Jetpack.
378 */
379 if ( 'vaultpress' === $module['module'] ) {
380 return false;
381 }
382
383 /*
384 * WooCommerce Analytics should only be available
385 * when running WooCommerce 3+
386 */
387 if (
388 'woocommerce-analytics' === $module['module']
389 && (
390 ! class_exists( 'WooCommerce' )
391 || version_compare( WC_VERSION, '3.0', '<' )
392 )
393 ) {
394 return false;
395 }
396
397 /*
398 * In Offline mode, modules that require a site or user
399 * level connection should be unavailable.
400 */
401 if ( ( new Status() )->is_offline_mode() ) {
402 return ! ( $module['requires_connection'] || $module['requires_user_connection'] );
403 }
404
405 /*
406 * Jetpack not connected.
407 */
408 if ( ! Jetpack::is_connection_ready() ) {
409 return false;
410 }
411
412 /*
413 * Jetpack connected at a site level only. Make sure to make
414 * modules that require a user connection unavailable.
415 */
416 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
417 return false;
418 }
419
420 return Jetpack_Plan::supports( $module['module'] );
421 }
422
423 /**
424 * Returns why a module is unavailable.
425 *
426 * @param array $module The module.
427 * @return string|false A string stating why the module is not available or false if the module is available.
428 */
429 public static function get_module_unavailable_reason( $module ) {
430 if ( ! is_array( $module ) || empty( $module ) ) {
431 return false;
432 }
433
434 if ( self::is_module_available( $module ) ) {
435 return false;
436 }
437
438 /**
439 * We never want to show VaultPress as activatable through Jetpack so return an empty string.
440 */
441 if ( 'vaultpress' === $module['module'] ) {
442 return '';
443 }
444
445 /*
446 * WooCommerce Analytics should only be available
447 * when running WooCommerce 3+
448 */
449 if (
450 'woocommerce-analytics' === $module['module']
451 && (
452 ! class_exists( 'WooCommerce' )
453 || version_compare( WC_VERSION, '3.0', '<' )
454 )
455 ) {
456 return __( 'Requires WooCommerce 3+ plugin', 'jetpack' );
457 }
458
459 /*
460 * In Offline mode, modules that require a site or user
461 * level connection should be unavailable.
462 */
463 if ( ( new Status() )->is_offline_mode() ) {
464 if ( $module['requires_connection'] || $module['requires_user_connection'] ) {
465 return __( 'Offline mode', 'jetpack' );
466 }
467 }
468
469 /*
470 * Jetpack not connected.
471 */
472 if ( ! Jetpack::is_connection_ready() ) {
473 return __( 'Jetpack is not connected', 'jetpack' );
474 }
475
476 /*
477 * Jetpack connected at a site level only and module requires a user connection.
478 */
479 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
480 return __( 'Requires a connected WordPress.com account', 'jetpack' );
481 }
482
483 /*
484 * Plan restrictions.
485 */
486 if ( ! Jetpack_Plan::supports( $module['module'] ) ) {
487 return __( 'Not supported by current plan', 'jetpack' );
488 }
489
490 return '';
491 }
492
493 /**
494 * Handle an unrecognized action.
495 *
496 * @param string $action Action.
497 */
498 public function handle_unrecognized_action( $action ) {
499 switch ( $action ) {
500 case 'bulk-activate':
501 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
502 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
503 break;
504 }
505
506 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
507 foreach ( $modules as $module ) {
508 Jetpack::log( 'activate', $module );
509 Jetpack::activate_module( $module, false );
510 }
511 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
512 wp_safe_redirect( wp_get_referer() );
513 exit;
514 case 'bulk-deactivate':
515 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
516 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
517 break;
518 }
519
520 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
521 foreach ( $modules as $module ) {
522 Jetpack::log( 'deactivate', $module );
523 Jetpack::deactivate_module( $module );
524 Jetpack::state( 'message', 'module_deactivated' );
525 }
526 Jetpack::state( 'module', $modules );
527 wp_safe_redirect( wp_get_referer() );
528 exit;
529 default:
530 return;
531 }
532 }
533
534 /**
535 * Fix redirect.
536 *
537 * Apparently we redirect to the referrer instead of whatever WordPress
538 * wants to redirect to when activating and deactivating modules.
539 *
540 * @param string $module Module slug.
541 * @param bool $redirect Should we exit after the module has been activated. Default to true.
542 */
543 public function fix_redirect( $module, $redirect = true ) {
544 if ( ! $redirect ) {
545 return;
546 }
547 if ( wp_get_referer() ) {
548 add_filter( 'wp_redirect', 'wp_get_referer' );
549 }
550 }
551
552 /**
553 * Add debugger admin menu.
554 */
555 public function admin_menu_debugger() {
556 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
557 Jetpack_Debugger::disconnect_and_redirect();
558 $debugger_hook = add_submenu_page(
559 '',
560 __( 'Debugging Center', 'jetpack' ),
561 '',
562 'manage_options',
563 'jetpack-debugger',
564 array( $this, 'wrap_debugger_page' )
565 );
566 add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
567 }
568
569 /**
570 * Wrap debugger page.
571 */
572 public function wrap_debugger_page() {
573 nocache_headers();
574 if ( ! current_user_can( 'manage_options' ) ) {
575 die( '-1' );
576 }
577 Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ), array( 'is-wide' => true ) );
578 }
579
580 /**
581 * Display debugger page.
582 */
583 public function debugger_page() {
584 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
585 Jetpack_Debugger::jetpack_debug_display_handler();
586 }
587
588 /**
589 * Determines if JITMs should display on a particular screen.
590 *
591 * @param bool $value The default value of the filter.
592 * @param string $screen_id The ID of the screen being tested for JITM display.
593 *
594 * @return bool True if JITMs should display, false otherwise.
595 */
596 public function should_display_jitms_on_screen( $value, $screen_id ) {
597 // Disable all JITMs on these pages.
598 if (
599 in_array(
600 $screen_id,
601 array(
602 'jetpack_page_akismet-key-config',
603 'admin_page_jetpack_modules',
604 ),
605 true
606 ) ) {
607 return false;
608 }
609
610 // Disable all JITMs on pages where the recommendations banner is displaying.
611 if (
612 in_array(
613 $screen_id,
614 array(
615 'dashboard',
616 'plugins',
617 'jetpack_page_stats',
618 ),
619 true
620 )
621 && \Jetpack_Recommendations_Banner::can_be_displayed()
622 ) {
623 return false;
624 }
625
626 return $value;
627 }
628 }
629 Jetpack_Admin::init();
630