Completed
Push — fix/at-remove-akismet-jetpack-... ( c1188b )
by
unknown
10:04
created

Jetpack_Admin::akismet_logo_replacement_styles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
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();
0 ignored issues
show
Bug introduced by
The property jetpack_react does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
33
34
		jetpack_require_lib( 'admin-pages/class.jetpack-settings-page' );
35
		$this->fallback_page = new Jetpack_Settings_Page();
0 ignored issues
show
Bug introduced by
The property fallback_page does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
36
37
		jetpack_require_lib( 'admin-pages/class-jetpack-about-page' );
38
		$this->jetpack_about = new Jetpack_About_Page();
0 ignored issues
show
Bug introduced by
The property jetpack_about does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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
			} elseif ( jetpack_is_atomic_site() ) { // We remove 'akismet-key-config' page only for AT sites.
78
				add_action(
79
					'jetpack_admin_menu',
80
					static function () {
81
						remove_submenu_page( 'jetpack', 'akismet-key-config' );
82
					},
83
					11
84
				);
85
			}
86
		}
87
88
		add_filter( 'jetpack_display_jitms_on_screen', array( $this, 'should_display_jitms_on_screen' ), 10, 2 );
89
	}
90
91
	/**
92
	 * Generate styles to replace Akismet logo for the Jetpack logo. It's a workaround until we create a proper settings page for
93
	 * Jetpack Anti-Spam. Without this, we would have to change the logo from Akismet codebase and we want to avoid that.
94
	 */
95
	public function akismet_logo_replacement_styles() {
96
		$logo            = new Jetpack_Logo();
97
		$logo_base64     = base64_encode( $logo->get_jp_emblem_larger() );
98
		$logo_base64_url = "data:image/svg+xml;base64,{$logo_base64}";
99
		$style           = ".akismet-masthead__logo-container { background: url({$logo_base64_url}) no-repeat .25rem; height: 1.8125rem; } .akismet-masthead__logo { display: none; }";
100
		wp_add_inline_style( 'admin-bar', $style );
101
	}
102
103 View Code Duplication
	static function sort_requires_connection_last( $module1, $module2 ) {
104
		if ( $module1['requires_connection'] == $module2['requires_connection'] ) {
105
			return 0;
106
		} elseif ( $module1['requires_connection'] ) {
107
			return 1;
108
		} elseif ( $module2['requires_connection'] ) {
109
			return -1;
110
		}
111
112
		return 0;
113
	}
114
115
	// Produce JS understandable objects of modules containing information for
116
	// presentation like description, name, configuration url, etc.
117
	function get_modules() {
118
		include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
119
		$available_modules = Jetpack::get_available_modules();
120
		$active_modules    = Jetpack::get_active_modules();
121
		$modules           = array();
122
		$jetpack_active    = Jetpack::is_connection_ready() || ( new Status() )->is_offline_mode();
123
		$overrides         = Jetpack_Modules_Overrides::instance();
124
		foreach ( $available_modules as $module ) {
125
			if ( $module_array = Jetpack::get_module( $module ) ) {
126
				/**
127
				 * Filters each module's short description.
128
				 *
129
				 * @since 3.0.0
130
				 *
131
				 * @param string $module_array['description'] Module description.
132
				 * @param string $module Module slug.
133
				 */
134
				$short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $module.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
135
				// Fix: correct multibyte strings truncate with checking for mbstring extension
136
				$short_desc_trunc = ( function_exists( 'mb_strlen' ) )
137
							? ( ( mb_strlen( $short_desc ) > 143 )
138
								? mb_substr( $short_desc, 0, 140 ) . '...'
139
								: $short_desc )
140
							: ( ( strlen( $short_desc ) > 143 )
141
								? substr( $short_desc, 0, 140 ) . '...'
142
								: $short_desc );
143
144
				$module_array['module'] = $module;
145
146
				$is_available = self::is_module_available( $module_array );
147
148
				$module_array['activated']          = ( $jetpack_active ? in_array( $module, $active_modules, true ) : false );
149
				$module_array['deactivate_nonce']   = wp_create_nonce( 'jetpack_deactivate-' . $module );
150
				$module_array['activate_nonce']     = wp_create_nonce( 'jetpack_activate-' . $module );
151
				$module_array['available']          = $is_available;
152
				$module_array['unavailable_reason'] = $is_available ? false : self::get_module_unavailable_reason( $module_array );
153
				$module_array['short_description']  = $short_desc_trunc;
154
				$module_array['configure_url']      = Jetpack::module_configuration_url( $module );
155
				$module_array['override']           = $overrides->get_module_override( $module );
156
157
				ob_start();
158
				/**
159
				 * Allow the display of a "Learn More" button.
160
				 * The dynamic part of the action, $module, is the module slug.
161
				 *
162
				 * @since 3.0.0
163
				 */
164
				do_action( 'jetpack_learn_more_button_' . $module );
165
				$module_array['learn_more_button'] = ob_get_clean();
166
167
				ob_start();
168
				/**
169
				 * Allow the display of information text when Jetpack is connected to WordPress.com.
170
				 * The dynamic part of the action, $module, is the module slug.
171
				 *
172
				 * @since 3.0.0
173
				 */
174
				do_action( 'jetpack_module_more_info_' . $module );
175
176
				/**
177
				* Filter the long description of a module.
178
				*
179
				* @since 3.5.0
180
				*
181
				* @param string ob_get_clean() The module long description.
182
				* @param string $module The module name.
183
				*/
184
				$module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $module.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
185
186
				ob_start();
187
				/**
188
				 * Filter the search terms for a module
189
				 *
190
				 * Search terms are typically added to the module headers, under "Additional Search Queries".
191
				 *
192
				 * Use syntax:
193
				 * function jetpack_$module_search_terms( $terms ) {
194
				 *  $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
195
				 *  return $terms;
196
				 * }
197
				 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
198
				 *
199
				 * @since 3.5.0
200
				 *
201
				 * @param string The search terms (comma separated).
202
				 */
203
				echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] );
204
				$module_array['search_terms'] = ob_get_clean();
205
206
				$module_array['configurable'] = false;
207
				if (
208
					current_user_can( 'manage_options' ) &&
209
					/**
210
					 * Allow the display of a configuration link in the Jetpack Settings screen.
211
					 *
212
					 * @since 3.0.0
213
					 *
214
					 * @param string $module Module name.
215
					 * @param bool false Should the Configure module link be displayed? Default to false.
216
					 */
217
					apply_filters( 'jetpack_module_configurable_' . $module, false )
218
				) {
219
					$module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
220
				}
221
222
				$modules[ $module ] = $module_array;
223
			}
224
		}
225
226
		uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
227
228
		if ( ! Jetpack::is_connection_ready() ) {
229
			uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
230
		}
231
232
		return $modules;
233
	}
234
235
	static function is_module_available( $module ) {
236
		if ( ! is_array( $module ) || empty( $module ) ) {
237
			return false;
238
		}
239
240
		/**
241
		 * We never want to show VaultPress as activatable through Jetpack.
242
		 */
243
		if ( 'vaultpress' === $module['module'] ) {
244
			return false;
245
		}
246
247
		/*
248
		 * WooCommerce Analytics should only be available
249
		 * when running WooCommerce 3+
250
		 */
251 View Code Duplication
		if (
252
			'woocommerce-analytics' === $module['module']
253
			&& (
254
				! class_exists( 'WooCommerce' )
255
				|| version_compare( WC_VERSION, '3.0', '<' )
256
			)
257
		) {
258
			return false;
259
		}
260
261
		/*
262
		 * In Offline mode, modules that require a site or user
263
		 * level connection should be unavailable.
264
		 */
265
		if ( ( new Status() )->is_offline_mode() ) {
266
			return ! ( $module['requires_connection'] || $module['requires_user_connection'] );
267
		}
268
269
		/*
270
		 * Jetpack not connected.
271
		 */
272
		if ( ! Jetpack::is_connection_ready() ) {
273
			return false;
274
		}
275
276
		/*
277
		 * Jetpack connected at a site level only. Make sure to make
278
		 * modules that require a user connection unavailable.
279
		 */
280
		if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
281
			return false;
282
		}
283
284
		return Jetpack_Plan::supports( $module['module'] );
285
286
	}
287
288
	/**
289
	 * Returns why a module is unavailable.
290
	 *
291
	 * @param  array $module The module.
292
	 * @return string|false A string stating why the module is not available or false if the module is available.
293
	 */
294
	public static function get_module_unavailable_reason( $module ) {
295
		if ( ! is_array( $module ) || empty( $module ) ) {
296
			return false;
297
		}
298
299
		if ( self::is_module_available( $module ) ) {
300
			return false;
301
		}
302
303
		/**
304
		 * We never want to show VaultPress as activatable through Jetpack so return an empty string.
305
		 */
306
		if ( 'vaultpress' === $module['module'] ) {
307
			return '';
308
		}
309
310
		/*
311
		 * WooCommerce Analytics should only be available
312
		 * when running WooCommerce 3+
313
		 */
314 View Code Duplication
		if (
315
			'woocommerce-analytics' === $module['module']
316
			&& (
317
					! class_exists( 'WooCommerce' )
318
					|| version_compare( WC_VERSION, '3.0', '<' )
319
				)
320
			) {
321
			return __( 'Requires WooCommerce 3+ plugin', 'jetpack' );
322
		}
323
324
		/*
325
		 * In Offline mode, modules that require a site or user
326
		 * level connection should be unavailable.
327
		 */
328
		if ( ( new Status() )->is_offline_mode() ) {
329
			if ( $module['requires_connection'] || $module['requires_user_connection'] ) {
330
				return __( 'Offline mode', 'jetpack' );
331
			}
332
		}
333
334
		/*
335
		 * Jetpack not connected.
336
		 */
337
		if ( ! Jetpack::is_connection_ready() ) {
338
			return __( 'Jetpack is not connected', 'jetpack' );
339
		}
340
341
		/*
342
		 * Jetpack connected at a site level only and module requires a user connection.
343
		 */
344
		if ( ! Jetpack::connection()->has_connected_owner() && $module['requires_user_connection'] ) {
345
			return __( 'Requires a connected WordPress.com account', 'jetpack' );
346
		}
347
348
		/*
349
		 * Plan restrictions.
350
		 */
351
		if ( ! Jetpack_Plan::supports( $module['module'] ) ) {
352
			return __( 'Not supported by current plan', 'jetpack' );
353
		}
354
355
		return '';
356
	}
357
358
	function handle_unrecognized_action( $action ) {
359
		switch ( $action ) {
360
			case 'bulk-activate':
361
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
362
					break;
363
				}
364
365
				$modules = (array) $_GET['modules'];
366
				$modules = array_map( 'sanitize_key', $modules );
367
				check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
368
				foreach ( $modules as $module ) {
369
					Jetpack::log( 'activate', $module );
370
					Jetpack::activate_module( $module, false );
371
				}
372
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
373
				wp_safe_redirect( wp_get_referer() );
374
				exit;
375 View Code Duplication
			case 'bulk-deactivate':
376
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
377
					break;
378
				}
379
380
				$modules = (array) $_GET['modules'];
381
				$modules = array_map( 'sanitize_key', $modules );
382
				check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
383
				foreach ( $modules as $module ) {
384
					Jetpack::log( 'deactivate', $module );
385
					Jetpack::deactivate_module( $module );
386
					Jetpack::state( 'message', 'module_deactivated' );
387
				}
388
				Jetpack::state( 'module', $modules );
0 ignored issues
show
Documentation introduced by
$modules is of type array, but the function expects a string|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
389
				wp_safe_redirect( wp_get_referer() );
390
				exit;
391
			default:
392
				return;
393
		}
394
	}
395
396
	function fix_redirect( $module, $redirect = true ) {
397
		if ( ! $redirect ) {
398
			return;
399
		}
400
		if ( wp_get_referer() ) {
401
			add_filter( 'wp_redirect', 'wp_get_referer' );
402
		}
403
	}
404
405
	function admin_menu_debugger() {
406
		jetpack_require_lib( 'debugger' );
407
		Jetpack_Debugger::disconnect_and_redirect();
408
		$debugger_hook = add_submenu_page(
409
			null,
410
			__( 'Debugging Center', 'jetpack' ),
411
			'',
412
			'manage_options',
413
			'jetpack-debugger',
414
			array( $this, 'wrap_debugger_page' )
415
		);
416
		add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
417
	}
418
419
	function wrap_debugger_page() {
420
		nocache_headers();
421
		if ( ! current_user_can( 'manage_options' ) ) {
422
			die( '-1' );
423
		}
424
		Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ) );
425
	}
426
427
	function debugger_page() {
428
		jetpack_require_lib( 'debugger' );
429
		Jetpack_Debugger::jetpack_debug_display_handler();
430
	}
431
432
	/**
433
	 * Determines if JITMs should display on a particular screen.
434
	 *
435
	 * @param bool   $value The default value of the filter.
436
	 * @param string $screen_id The ID of the screen being tested for JITM display.
437
	 *
438
	 * @return bool True if JITMs should display, false otherwise.
439
	 */
440
	public function should_display_jitms_on_screen( $value, $screen_id ) {
441
		// Disable all JITMs on these pages.
442
		if (
443
		in_array(
444
			$screen_id,
445
			array(
446
				'jetpack_page_akismet-key-config',
447
				'admin_page_jetpack_modules',
448
			),
449
			true
450
		) ) {
451
			return false;
452
		}
453
454
		// Disable all JITMs on pages where the recommendations banner is displaying.
455
		if (
456
			in_array(
457
				$screen_id,
458
				array(
459
					'dashboard',
460
					'plugins',
461
					'jetpack_page_stats',
462
				),
463
				true
464
			)
465
			&& \Jetpack_Recommendations_Banner::can_be_displayed()
466
		) {
467
			return false;
468
		}
469
470
		return $value;
471
	}
472
}
473
Jetpack_Admin::init();
474