PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.8.3
Jetpack – WP Security, Backup, Speed, & Growth v13.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 1 year ago css 2 years ago extensions 1 year ago images 1 year ago jetpack_vendor 1 day ago json-endpoints 1 year ago modules 1 year ago sal 2 years ago src 2 years ago vendor 1 year ago views 2 years ago CHANGELOG.md 1 year 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 1 year ago class.frame-nonce-preview.php 4 years ago class.jetpack-admin.php 1 year 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 1 year 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 1 year ago class.json-api-endpoints.php 2 years ago class.json-api.php 2 years ago class.photon.php 3 years ago composer.json 1 year 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 1 day 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 1 day ago uninstall.php 2 years ago wpml-config.xml 4 years ago
class.jetpack-admin.php
616 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( 'admin_menu', array( $jetpack_react, 'remove_jetpack_menu' ), 2000 );
68 add_action( 'jetpack_admin_menu', array( $jetpack_react, 'jetpack_add_dashboard_sub_nav_item' ) );
69 add_action( 'jetpack_admin_menu', array( $jetpack_react, 'jetpack_add_settings_sub_nav_item' ) );
70 add_action( 'jetpack_admin_menu', array( $this, 'admin_menu_debugger' ) );
71 add_action( 'jetpack_admin_menu', array( $fallback_page, 'add_actions' ) );
72 add_action( 'jetpack_admin_menu', array( $jetpack_about, 'add_actions' ) );
73
74 // Add redirect to current page for activation/deactivation of modules.
75 add_action( 'jetpack_pre_activate_module', array( $this, 'fix_redirect' ), 10, 2 );
76 add_action( 'jetpack_pre_deactivate_module', array( $this, 'fix_redirect' ), 10, 2 );
77
78 // Add module bulk actions handler.
79 add_action( 'jetpack_unrecognized_action', array( $this, 'handle_unrecognized_action' ) );
80
81 if ( class_exists( 'Akismet_Admin' ) ) {
82 // If the site has Jetpack Anti-spam, change the Akismet menu label and logo accordingly.
83 $site_products = array_column( Jetpack_Plan::get_products(), 'product_slug' );
84 $has_anti_spam_product = count( array_intersect( array( 'jetpack_anti_spam', 'jetpack_anti_spam_monthly' ), $site_products ) ) > 0;
85
86 if ( Jetpack_Plan::supports( 'akismet' ) || Jetpack_Plan::supports( 'antispam' ) || $has_anti_spam_product ) {
87 // Prevent Akismet from adding a menu item.
88 add_action(
89 'admin_menu',
90 function () {
91 remove_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 );
92 },
93 4
94 );
95
96 // Add an Anti-spam menu item for Jetpack. This is handled automatically by the Admin_Menu as long as it has been initialized.
97 Admin_Menu::init();
98 add_action( 'admin_enqueue_scripts', array( $this, 'akismet_logo_replacement_styles' ) );
99 }
100 }
101
102 // Ensure an Additional CSS menu item is added to the Appearance menu whenever Jetpack is connected.
103 add_action( 'admin_menu', array( $this, 'additional_css_menu' ) );
104
105 add_filter( 'jetpack_display_jitms_on_screen', array( $this, 'should_display_jitms_on_screen' ), 10, 2 );
106
107 // Register Jetpack partner coupon hooks.
108 Jetpack_Partner_Coupon::register_coupon_admin_hooks( 'jetpack', Jetpack::admin_url() );
109 }
110
111 /**
112 * Generate styles to replace Akismet logo for the Jetpack Akismet Anti-spam logo.
113 Without this, we would have to change the logo from Akismet codebase and we want to avoid that.
114 */
115 public function akismet_logo_replacement_styles() {
116 $logo_url = esc_url( plugins_url( 'images/products/logo-anti-spam.svg', JETPACK__PLUGIN_FILE ) );
117 $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; }";
118 $style .= '@media screen and (max-width: 782px) { .akismet-masthead__logo-container { margin-left: 4px; } }';
119 wp_add_inline_style( 'admin-bar', $style );
120 }
121
122 /**
123 * Handle our Additional CSS menu item and legacy page declaration.
124 *
125 * @since 11.0 . Prior to that, this function was located in custom-css-4.7.php (now custom-css.php).
126 */
127 public static function additional_css_menu() {
128 /*
129 * Custom CSS for the Customizer is deprecated for block themes as of WP 6.1, so we only expose it with a menu
130 * if the site already has existing CSS code.
131 */
132 if ( wp_is_block_theme() ) {
133 $styles = wp_get_custom_css();
134 if ( ! $styles ) {
135 return;
136 }
137 }
138
139 // If the site is a WoA site and the custom-css feature is not available, return.
140 // See https://github.com/Automattic/jetpack/pull/19965 for more on how this menu item is dealt with on WoA sites.
141 if ( ( new Host() )->is_woa_site() && ! ( in_array( 'custom-css', Jetpack::get_available_modules(), true ) ) ) {
142 return;
143 } elseif (
144 class_exists( 'Jetpack' ) && (
145 Jetpack::is_module_active( 'custom-css' ) || // If the Custom CSS module is enabled, add the Additional CSS menu item and link to the Customizer.
146 ( wp_is_block_theme() && ! empty( wp_get_custom_css() ) ) // Do the same if the theme is block-based but has existing custom CSS.
147 )
148 ) {
149 // Add in our legacy page to support old bookmarks and such.
150 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'customizer_redirect' ) );
151
152 // Add in our new page slug that will redirect to the customizer.
153 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-customizer-redirect', array( __CLASS__, 'customizer_redirect' ) );
154 add_action( "load-{$hook}", array( __CLASS__, 'customizer_redirect' ) );
155 } elseif ( class_exists( 'Jetpack' ) && Jetpack::is_connection_ready() ) { // Link to the Jetpack Settings > Writing page, highlighting the Custom CSS setting.
156 add_submenu_page( '', __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss', array( __CLASS__, 'theme_enhancements_redirect' ) );
157
158 $hook = add_theme_page( __( 'CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'edit_theme_options', 'editcss-theme-enhancements-redirect', array( __CLASS__, 'theme_enhancements_redirect' ) );
159 add_action( "load-{$hook}", array( __CLASS__, 'theme_enhancements_redirect' ) );
160 }
161 }
162
163 /**
164 * Handle the redirect for the customizer. This is necessary because
165 * we can't directly add customizer links to the admin menu.
166 *
167 * @since 11.0 . Prior to that, this function was located in custom-css-4.7.php (now custom-css.php).
168 *
169 * There is a core patch in trac that would make this unnecessary.
170 *
171 * @link https://core.trac.wordpress.org/ticket/39050
172 *
173 * @return never
174 */
175 public static function customizer_redirect() {
176 wp_safe_redirect(
177 self::customizer_link(
178 array(
179 'return_url' => wp_get_referer(),
180 )
181 )
182 );
183 exit;
184 }
185
186 /**
187 * Handle the Additional CSS redirect to the Jetpack settings Theme Enhancements section.
188 *
189 * @since 11.0
190 *
191 * @return never
192 */
193 public static function theme_enhancements_redirect() {
194 wp_safe_redirect(
195 'admin.php?page=jetpack#/writing?term=custom-css'
196 );
197 exit;
198 }
199
200 /**
201 * Build the URL to deep link to the Customizer.
202 *
203 * You can modify the return url via $args.
204 *
205 * @since 11.0 in this file. This method is also located in custom-css-4.7.php to cover legacy scenarios.
206 *
207 * @param array $args Array of parameters.
208 * @return string
209 */
210 public static function customizer_link( $args = array() ) {
211 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
212 $args = wp_parse_args(
213 $args,
214 array(
215 'return_url' => rawurlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
216 )
217 );
218 }
219
220 return add_query_arg(
221 array(
222 array(
223 'autofocus' => array(
224 'section' => 'custom_css',
225 ),
226 ),
227 'return' => $args['return_url'],
228 ),
229 admin_url( 'customize.php' )
230 );
231 }
232
233 /**
234 * Sort callback to put modules with `requires_connection` last.
235 *
236 * @param array $module1 Module data.
237 * @param array $module2 Module data.
238 * @return int Indicating the relative ordering of module1 and module2.
239 */
240 public static function sort_requires_connection_last( $module1, $module2 ) {
241 return ( (bool) $module1['requires_connection'] ) <=> ( (bool) $module2['requires_connection'] );
242 }
243
244 /**
245 * Produce JS understandable objects of modules containing information for
246 * presentation like description, name, configuration url, etc.
247 */
248 public function get_modules() {
249 include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
250 $available_modules = Jetpack::get_available_modules();
251 $active_modules = Jetpack::get_active_modules();
252 $modules = array();
253 $jetpack_active = Jetpack::is_connection_ready() || ( new Status() )->is_offline_mode();
254 $overrides = Jetpack_Modules_Overrides::instance();
255 foreach ( $available_modules as $module ) {
256 $module_array = Jetpack::get_module( $module );
257 if ( $module_array ) {
258 /**
259 * Filters each module's short description.
260 *
261 * @since 3.0.0
262 *
263 * @param string $module_array['description'] Module description.
264 * @param string $module Module slug.
265 */
266 $short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
267 // Fix: correct multibyte strings truncate with checking for mbstring extension.
268 $short_desc_trunc = ( function_exists( 'mb_strlen' ) )
269 ? ( ( mb_strlen( $short_desc ) > 143 )
270 ? mb_substr( $short_desc, 0, 140 ) . '...'
271 : $short_desc )
272 : ( ( strlen( $short_desc ) > 143 )
273 ? substr( $short_desc, 0, 140 ) . '...'
274 : $short_desc );
275
276 $module_array['module'] = $module;
277
278 $is_available = self::is_module_available( $module_array );
279
280 $module_array['activated'] = ( $jetpack_active ? in_array( $module, $active_modules, true ) : false );
281 $module_array['deactivate_nonce'] = wp_create_nonce( 'jetpack_deactivate-' . $module );
282 $module_array['activate_nonce'] = wp_create_nonce( 'jetpack_activate-' . $module );
283 $module_array['available'] = $is_available;
284 $module_array['unavailable_reason'] = $is_available ? false : self::get_module_unavailable_reason( $module_array );
285 $module_array['short_description'] = $short_desc_trunc;
286 $module_array['configure_url'] = Jetpack::module_configuration_url( $module );
287 $module_array['override'] = $overrides->get_module_override( $module );
288 $module_array['disabled'] = $is_available ? '' : 'disabled="disabled"';
289
290 ob_start();
291 /**
292 * Allow the display of a "Learn More" button.
293 * The dynamic part of the action, $module, is the module slug.
294 *
295 * @since 3.0.0
296 */
297 do_action( 'jetpack_learn_more_button_' . $module );
298 $module_array['learn_more_button'] = ob_get_clean();
299
300 ob_start();
301 /**
302 * Allow the display of information text when Jetpack is connected to WordPress.com.
303 * The dynamic part of the action, $module, is the module slug.
304 *
305 * @since 3.0.0
306 */
307 do_action( 'jetpack_module_more_info_' . $module );
308
309 /**
310 * Filter the long description of a module.
311 *
312 * @since 3.5.0
313 *
314 * @param string ob_get_clean() The module long description.
315 * @param string $module The module name.
316 */
317 $module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
318
319 ob_start();
320 /**
321 * Filter the search terms for a module
322 *
323 * Search terms are typically added to the module headers, under "Additional Search Queries".
324 *
325 * Use syntax:
326 * function jetpack_$module_search_terms( $terms ) {
327 * $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
328 * return $terms;
329 * }
330 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
331 *
332 * @since 3.5.0
333 *
334 * @param string The search terms (comma separated).
335 */
336 echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
337 $module_array['search_terms'] = ob_get_clean();
338
339 $module_array['configurable'] = false;
340 if (
341 current_user_can( 'manage_options' ) &&
342 /**
343 * Allow the display of a configuration link in the Jetpack Settings screen.
344 *
345 * @since 3.0.0
346 *
347 * @param string $module Module name.
348 * @param bool false Should the Configure module link be displayed? Default to false.
349 */
350 apply_filters( 'jetpack_module_configurable_' . $module, false )
351 ) {
352 $module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
353 }
354
355 $modules[ $module ] = $module_array;
356 }
357 }
358
359 uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
360
361 if ( ! Jetpack::is_connection_ready() ) {
362 uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
363 }
364
365 return $modules;
366 }
367
368 /**
369 * Check if a module is available.
370 *
371 * @param array $module Module data.
372 */
373 public static function is_module_available( $module ) {
374 if ( ! is_array( $module ) || empty( $module ) ) {
375 return false;
376 }
377
378 /**
379 * We never want to show VaultPress as activatable through Jetpack.
380 */
381 if ( 'vaultpress' === $module['module'] ) {
382 return false;
383 }
384
385 /*
386 * WooCommerce Analytics should only be available
387 * when running WooCommerce 3+
388 */
389 if (
390 'woocommerce-analytics' === $module['module']
391 && (
392 ! class_exists( 'WooCommerce' )
393 || version_compare( WC_VERSION, '3.0', '<' )
394 )
395 ) {
396 return false;
397 }
398
399 /*
400 * In Offline mode, modules that require a site or user
401 * level connection should be unavailable.
402 */
403 if ( ( new Status() )->is_offline_mode() ) {
404 return ! ( $module['requires_connection'] || $module['requires_user_connection'] );
405 }
406
407 /*
408 * Jetpack not connected.
409 */
410 if ( ! Jetpack::is_connection_ready() ) {
411 return false;
412 }
413
414 /*
415 * Jetpack connected at a site level only. Make sure to make
416 * modules that require a user connection unavailable.
417 */
418 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
419 return false;
420 }
421
422 return Jetpack_Plan::supports( $module['module'] );
423 }
424
425 /**
426 * Returns why a module is unavailable.
427 *
428 * @param array $module The module.
429 * @return string|false A string stating why the module is not available or false if the module is available.
430 */
431 public static function get_module_unavailable_reason( $module ) {
432 if ( ! is_array( $module ) || empty( $module ) ) {
433 return false;
434 }
435
436 if ( self::is_module_available( $module ) ) {
437 return false;
438 }
439
440 /**
441 * We never want to show VaultPress as activatable through Jetpack so return an empty string.
442 */
443 if ( 'vaultpress' === $module['module'] ) {
444 return '';
445 }
446
447 /*
448 * WooCommerce Analytics should only be available
449 * when running WooCommerce 3+
450 */
451 if (
452 'woocommerce-analytics' === $module['module']
453 && (
454 ! class_exists( 'WooCommerce' )
455 || version_compare( WC_VERSION, '3.0', '<' )
456 )
457 ) {
458 return __( 'Requires WooCommerce 3+ plugin', 'jetpack' );
459 }
460
461 /*
462 * In Offline mode, modules that require a site or user
463 * level connection should be unavailable.
464 */
465 if ( ( new Status() )->is_offline_mode() ) {
466 if ( $module['requires_connection'] || $module['requires_user_connection'] ) {
467 return __( 'Offline mode', 'jetpack' );
468 }
469 }
470
471 /*
472 * Jetpack not connected.
473 */
474 if ( ! Jetpack::is_connection_ready() ) {
475 return __( 'Jetpack is not connected', 'jetpack' );
476 }
477
478 /*
479 * Jetpack connected at a site level only and module requires a user connection.
480 */
481 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
482 return __( 'Requires a connected WordPress.com account', 'jetpack' );
483 }
484
485 /*
486 * Plan restrictions.
487 */
488 if ( ! Jetpack_Plan::supports( $module['module'] ) ) {
489 return __( 'Not supported by current plan', 'jetpack' );
490 }
491
492 return '';
493 }
494
495 /**
496 * Handle an unrecognized action.
497 *
498 * @param string $action Action.
499 */
500 public function handle_unrecognized_action( $action ) {
501 switch ( $action ) {
502 case 'bulk-activate':
503 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
504 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
505 break;
506 }
507
508 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
509 foreach ( $modules as $module ) {
510 Jetpack::log( 'activate', $module );
511 Jetpack::activate_module( $module, false );
512 }
513 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
514 wp_safe_redirect( wp_get_referer() );
515 exit;
516 case 'bulk-deactivate':
517 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
518 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
519 break;
520 }
521
522 $modules = isset( $_GET['modules'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_GET['modules'] ) ) : array();
523 foreach ( $modules as $module ) {
524 Jetpack::log( 'deactivate', $module );
525 Jetpack::deactivate_module( $module );
526 Jetpack::state( 'message', 'module_deactivated' );
527 }
528 Jetpack::state( 'module', $modules );
529 wp_safe_redirect( wp_get_referer() );
530 exit;
531 default:
532 return;
533 }
534 }
535
536 /**
537 * Fix redirect.
538 *
539 * Apparently we redirect to the referrer instead of whatever WordPress
540 * wants to redirect to when activating and deactivating modules.
541 *
542 * @param string $module Module slug.
543 * @param bool $redirect Should we exit after the module has been activated. Default to true.
544 */
545 public function fix_redirect( $module, $redirect = true ) {
546 if ( ! $redirect ) {
547 return;
548 }
549 if ( wp_get_referer() ) {
550 add_filter( 'wp_redirect', 'wp_get_referer' );
551 }
552 }
553
554 /**
555 * Add debugger admin menu.
556 */
557 public function admin_menu_debugger() {
558 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
559 Jetpack_Debugger::disconnect_and_redirect();
560 $debugger_hook = add_submenu_page(
561 '',
562 __( 'Debugging Center', 'jetpack' ),
563 '',
564 'manage_options',
565 'jetpack-debugger',
566 array( $this, 'wrap_debugger_page' )
567 );
568 add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
569 }
570
571 /**
572 * Wrap debugger page.
573 */
574 public function wrap_debugger_page() {
575 nocache_headers();
576 if ( ! current_user_can( 'manage_options' ) ) {
577 die( '-1' );
578 }
579 Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ), array( 'is-wide' => true ) );
580 }
581
582 /**
583 * Display debugger page.
584 */
585 public function debugger_page() {
586 require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
587 Jetpack_Debugger::jetpack_debug_display_handler();
588 }
589
590 /**
591 * Determines if JITMs should display on a particular screen.
592 *
593 * @param bool $value The default value of the filter.
594 * @param string $screen_id The ID of the screen being tested for JITM display.
595 *
596 * @return bool True if JITMs should display, false otherwise.
597 */
598 public function should_display_jitms_on_screen( $value, $screen_id ) {
599 // Disable all JITMs on these pages.
600 if (
601 in_array(
602 $screen_id,
603 array(
604 'jetpack_page_akismet-key-config',
605 'admin_page_jetpack_modules',
606 ),
607 true
608 ) ) {
609 return false;
610 }
611
612 return $value;
613 }
614 }
615 Jetpack_Admin::init();
616