PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.6.1
Jetpack – WP Security, Backup, Speed, & Growth v9.6.1
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 All 500 releases
jetpack / class.jetpack-admin.php
class.jetpack-admin.php
392 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Status;
4 use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
5
6 // Build the Jetpack admin menu as a whole
7 class Jetpack_Admin {
8
9 /**
10 * @var Jetpack_Admin
11 **/
12 private static $instance = null;
13
14 static function init() {
15 if ( isset( $_GET['page'] ) && $_GET['page'] === 'jetpack' ) {
16 add_filter( 'nocache_headers', array( 'Jetpack_Admin', 'add_no_store_header' ), 100 );
17 }
18
19 if ( is_null( self::$instance ) ) {
20 self::$instance = new Jetpack_Admin();
21 }
22 return self::$instance;
23 }
24
25 static function add_no_store_header( $headers ) {
26 $headers['Cache-Control'] .= ', no-store';
27 return $headers;
28 }
29
30 private function __construct() {
31 jetpack_require_lib( 'admin-pages/class.jetpack-react-page' );
32 $this->jetpack_react = new Jetpack_React_Page();
33
34 jetpack_require_lib( 'admin-pages/class.jetpack-settings-page' );
35 $this->fallback_page = new Jetpack_Settings_Page();
36
37 jetpack_require_lib( 'admin-pages/class-jetpack-about-page' );
38 $this->jetpack_about = new Jetpack_About_Page();
39
40 add_action( 'admin_menu', array( $this->jetpack_react, 'add_actions' ), 998 );
41 add_action( 'admin_menu', array( $this->jetpack_react, 'add_actions' ), 998 );
42 add_action( 'jetpack_admin_menu', array( $this->jetpack_react, 'jetpack_add_dashboard_sub_nav_item' ) );
43 add_action( 'jetpack_admin_menu', array( $this->jetpack_react, 'jetpack_add_settings_sub_nav_item' ) );
44 add_action( 'jetpack_admin_menu', array( $this, 'admin_menu_debugger' ) );
45 add_action( 'jetpack_admin_menu', array( $this->fallback_page, 'add_actions' ) );
46 add_action( 'jetpack_admin_menu', array( $this->jetpack_about, 'add_actions' ) );
47
48 // Add redirect to current page for activation/deactivation of modules
49 add_action( 'jetpack_pre_activate_module', array( $this, 'fix_redirect' ), 10, 2 );
50 add_action( 'jetpack_pre_deactivate_module', array( $this, 'fix_redirect' ) );
51
52 // Add module bulk actions handler
53 add_action( 'jetpack_unrecognized_action', array( $this, 'handle_unrecognized_action' ) );
54
55 if ( class_exists( 'Akismet_Admin' ) ) {
56 // If the site has Jetpack Anti-Spam, change the Akismet menu label accordingly.
57 $site_products = Jetpack_Plan::get_products();
58 $anti_spam_products = array( 'jetpack_anti_spam_monthly', 'jetpack_anti_spam' );
59 if ( ! empty( array_intersect( $anti_spam_products, array_column( $site_products, 'product_slug' ) ) ) ) {
60 // Prevent Akismet from adding a menu item.
61 add_action(
62 'admin_menu',
63 function () {
64 remove_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 );
65 },
66 4
67 );
68
69 // Add an Anti-spam menu item for Jetpack.
70 add_action(
71 'jetpack_admin_menu',
72 function () {
73 add_submenu_page( 'jetpack', __( 'Anti-Spam', 'jetpack' ), __( 'Anti-Spam', 'jetpack' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
74 }
75 );
76 add_action( 'admin_enqueue_scripts', array( $this, 'akismet_logo_replacement_styles' ) );
77 }
78 }
79
80 add_filter( 'jetpack_display_jitms_on_screen', array( $this, 'should_display_jitms_on_screen' ), 10, 2 );
81 }
82
83 /**
84 * Generate styles to replace Akismet logo for the Jetpack logo. It's a workaround until we create a proper settings page for
85 * Jetpack Anti-Spam. Without this, we would have to change the logo from Akismet codebase and we want to avoid that.
86 */
87 public function akismet_logo_replacement_styles() {
88 $logo = new Jetpack_Logo();
89 $logo_base64 = base64_encode( $logo->get_jp_emblem_larger() );
90 $logo_base64_url = "data:image/svg+xml;base64,{$logo_base64}";
91 $style = ".akismet-masthead__logo-container { background: url({$logo_base64_url}) no-repeat .25rem; height: 1.8125rem; } .akismet-masthead__logo { display: none; }";
92 wp_add_inline_style( 'admin-bar', $style );
93 }
94
95 static function sort_requires_connection_last( $module1, $module2 ) {
96 if ( $module1['requires_connection'] == $module2['requires_connection'] ) {
97 return 0;
98 } elseif ( $module1['requires_connection'] ) {
99 return 1;
100 } elseif ( $module2['requires_connection'] ) {
101 return -1;
102 }
103
104 return 0;
105 }
106
107 // Produce JS understandable objects of modules containing information for
108 // presentation like description, name, configuration url, etc.
109 function get_modules() {
110 include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
111 $available_modules = Jetpack::get_available_modules();
112 $active_modules = Jetpack::get_active_modules();
113 $modules = array();
114 $jetpack_active = Jetpack::is_connection_ready() || ( new Status() )->is_offline_mode();
115 $overrides = Jetpack_Modules_Overrides::instance();
116 foreach ( $available_modules as $module ) {
117 if ( $module_array = Jetpack::get_module( $module ) ) {
118 /**
119 * Filters each module's short description.
120 *
121 * @since 3.0.0
122 *
123 * @param string $module_array['description'] Module description.
124 * @param string $module Module slug.
125 */
126 $short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
127 // Fix: correct multibyte strings truncate with checking for mbstring extension
128 $short_desc_trunc = ( function_exists( 'mb_strlen' ) )
129 ? ( ( mb_strlen( $short_desc ) > 143 )
130 ? mb_substr( $short_desc, 0, 140 ) . '...'
131 : $short_desc )
132 : ( ( strlen( $short_desc ) > 143 )
133 ? substr( $short_desc, 0, 140 ) . '...'
134 : $short_desc );
135
136 $module_array['module'] = $module;
137 $module_array['activated'] = ( $jetpack_active ? in_array( $module, $active_modules ) : false );
138 $module_array['deactivate_nonce'] = wp_create_nonce( 'jetpack_deactivate-' . $module );
139 $module_array['activate_nonce'] = wp_create_nonce( 'jetpack_activate-' . $module );
140 $module_array['available'] = self::is_module_available( $module_array );
141 $module_array['short_description'] = $short_desc_trunc;
142 $module_array['configure_url'] = Jetpack::module_configuration_url( $module );
143 $module_array['override'] = $overrides->get_module_override( $module );
144
145 ob_start();
146 /**
147 * Allow the display of a "Learn More" button.
148 * The dynamic part of the action, $module, is the module slug.
149 *
150 * @since 3.0.0
151 */
152 do_action( 'jetpack_learn_more_button_' . $module );
153 $module_array['learn_more_button'] = ob_get_clean();
154
155 ob_start();
156 /**
157 * Allow the display of information text when Jetpack is connected to WordPress.com.
158 * The dynamic part of the action, $module, is the module slug.
159 *
160 * @since 3.0.0
161 */
162 do_action( 'jetpack_module_more_info_' . $module );
163
164 /**
165 * Filter the long description of a module.
166 *
167 * @since 3.5.0
168 *
169 * @param string ob_get_clean() The module long description.
170 * @param string $module The module name.
171 */
172 $module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
173
174 ob_start();
175 /**
176 * Filter the search terms for a module
177 *
178 * Search terms are typically added to the module headers, under "Additional Search Queries".
179 *
180 * Use syntax:
181 * function jetpack_$module_search_terms( $terms ) {
182 * $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
183 * return $terms;
184 * }
185 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
186 *
187 * @since 3.5.0
188 *
189 * @param string The search terms (comma separated).
190 */
191 echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] );
192 $module_array['search_terms'] = ob_get_clean();
193
194 $module_array['configurable'] = false;
195 if (
196 current_user_can( 'manage_options' ) &&
197 /**
198 * Allow the display of a configuration link in the Jetpack Settings screen.
199 *
200 * @since 3.0.0
201 *
202 * @param string $module Module name.
203 * @param bool false Should the Configure module link be displayed? Default to false.
204 */
205 apply_filters( 'jetpack_module_configurable_' . $module, false )
206 ) {
207 $module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
208 }
209
210 $modules[ $module ] = $module_array;
211 }
212 }
213
214 uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
215
216 if ( ! Jetpack::is_connection_ready() ) {
217 uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
218 }
219
220 return $modules;
221 }
222
223 static function is_module_available( $module ) {
224 if ( ! is_array( $module ) || empty( $module ) ) {
225 return false;
226 }
227
228 /**
229 * We never want to show VaultPress as activatable through Jetpack.
230 */
231 if ( 'vaultpress' === $module['module'] ) {
232 return false;
233 }
234
235 /*
236 * WooCommerce Analytics should only be available
237 * when running WooCommerce 3+
238 */
239 if (
240 'woocommerce-analytics' === $module['module']
241 && (
242 ! class_exists( 'WooCommerce' )
243 || version_compare( WC_VERSION, '3.0', '<' )
244 )
245 ) {
246 return false;
247 }
248
249 /*
250 * In Offline mode, modules that require a site or user
251 * level connection should be unavailable.
252 */
253 if ( ( new Status() )->is_offline_mode() ) {
254 return ! ( $module['requires_connection'] || $module['requires_user_connection'] );
255 }
256
257 /*
258 * Jetpack not connected.
259 */
260 if ( ! Jetpack::is_connection_ready() ) {
261 return false;
262 }
263
264 /*
265 * Jetpack connected at a site level only. Make sure to make
266 * modules that require a user connection unavailable.
267 */
268 if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
269 return false;
270 }
271
272 return Jetpack_Plan::supports( $module['module'] );
273
274 }
275
276 function handle_unrecognized_action( $action ) {
277 switch ( $action ) {
278 case 'bulk-activate':
279 if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
280 break;
281 }
282
283 $modules = (array) $_GET['modules'];
284 $modules = array_map( 'sanitize_key', $modules );
285 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
286 foreach ( $modules as $module ) {
287 Jetpack::log( 'activate', $module );
288 Jetpack::activate_module( $module, false );
289 }
290 // The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
291 wp_safe_redirect( wp_get_referer() );
292 exit;
293 case 'bulk-deactivate':
294 if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
295 break;
296 }
297
298 $modules = (array) $_GET['modules'];
299 $modules = array_map( 'sanitize_key', $modules );
300 check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
301 foreach ( $modules as $module ) {
302 Jetpack::log( 'deactivate', $module );
303 Jetpack::deactivate_module( $module );
304 Jetpack::state( 'message', 'module_deactivated' );
305 }
306 Jetpack::state( 'module', $modules );
307 wp_safe_redirect( wp_get_referer() );
308 exit;
309 default:
310 return;
311 }
312 }
313
314 function fix_redirect( $module, $redirect = true ) {
315 if ( ! $redirect ) {
316 return;
317 }
318 if ( wp_get_referer() ) {
319 add_filter( 'wp_redirect', 'wp_get_referer' );
320 }
321 }
322
323 function admin_menu_debugger() {
324 jetpack_require_lib( 'debugger' );
325 Jetpack_Debugger::disconnect_and_redirect();
326 $debugger_hook = add_submenu_page(
327 null,
328 __( 'Debugging Center', 'jetpack' ),
329 '',
330 'manage_options',
331 'jetpack-debugger',
332 array( $this, 'wrap_debugger_page' )
333 );
334 add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
335 }
336
337 function wrap_debugger_page() {
338 nocache_headers();
339 if ( ! current_user_can( 'manage_options' ) ) {
340 die( '-1' );
341 }
342 Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ) );
343 }
344
345 function debugger_page() {
346 jetpack_require_lib( 'debugger' );
347 Jetpack_Debugger::jetpack_debug_display_handler();
348 }
349
350 /**
351 * Determines if JITMs should display on a particular screen.
352 *
353 * @param bool $value The default value of the filter.
354 * @param string $screen_id The ID of the screen being tested for JITM display.
355 *
356 * @return bool True if JITMs should display, false otherwise.
357 */
358 public function should_display_jitms_on_screen( $value, $screen_id ) {
359 // Disable all JITMs on these pages.
360 if (
361 in_array(
362 $screen_id,
363 array(
364 'jetpack_page_akismet-key-config',
365 'admin_page_jetpack_modules',
366 ),
367 true
368 ) ) {
369 return false;
370 }
371
372 // Disable all JITMs on pages where the recommendations banner is displaying.
373 if (
374 in_array(
375 $screen_id,
376 array(
377 'dashboard',
378 'plugins',
379 'jetpack_page_stats',
380 ),
381 true
382 )
383 && \Jetpack_Recommendations_Banner::can_be_displayed()
384 ) {
385 return false;
386 }
387
388 return $value;
389 }
390 }
391 Jetpack_Admin::init();
392