Completed
Push — update/switch-to-classic-edito... ( a3b284...5ef1b3 )
by
unknown
07:18
created

Jetpack_Admin   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 297
Duplicated Lines 9.09 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 27
loc 297
rs 8.64
c 0
b 0
f 0
wmc 47
lcom 1
cbo 9

12 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 10 4
A add_no_store_header() 0 4 1
A sort_requires_connection_last() 11 11 4
C get_modules() 0 113 11
A __construct() 0 28 1
A activate_classic_editor() 0 5 3
B is_module_available() 0 36 9
B handle_unrecognized_action() 16 37 7
A fix_redirect() 0 8 3
A admin_menu_debugger() 0 13 1
A wrap_debugger_page() 0 7 2
A debugger_page() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Admin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Admin, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use Automattic\Jetpack\Status;
4
5
// Build the Jetpack admin menu as a whole
6
class Jetpack_Admin {
7
8
	/**
9
	 * @var Jetpack_Admin
10
	 **/
11
	private static $instance = null;
12
13
	static function init() {
14
		if ( isset( $_GET['page'] ) && $_GET['page'] === 'jetpack' ) {
15
			add_filter( 'nocache_headers', array( 'Jetpack_Admin', 'add_no_store_header' ), 100 );
16
		}
17
18
		if ( is_null( self::$instance ) ) {
19
			self::$instance = new Jetpack_Admin();
20
		}
21
		return self::$instance;
22
	}
23
24
	static function add_no_store_header( $headers ) {
25
		$headers['Cache-Control'] .= ', no-store';
26
		return $headers;
27
	}
28
29
	private function __construct() {
30
		jetpack_require_lib( 'admin-pages/class.jetpack-react-page' );
31
		$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...
32
33
		jetpack_require_lib( 'admin-pages/class.jetpack-settings-page' );
34
		$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...
35
36
		jetpack_require_lib( 'admin-pages/class-jetpack-about-page' );
37
		$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...
38
39
		add_action( 'admin_menu', array( $this->jetpack_react, 'add_actions' ), 998 );
40
		add_action( 'admin_menu', array( $this->jetpack_react, 'add_actions' ), 998 );
41
		add_action( 'jetpack_admin_menu', array( $this->jetpack_react, 'jetpack_add_set_up_sub_nav_item' ) );
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
		add_action( 'admin_init', array( $this, 'activate_classic_editor' ), 9 );
56
	}
57
58
	public function activate_classic_editor() {
59
		if ( 'classic' === $_GET['set-editor']  && current_user_can( 'activate_plugin' ) ) {
60
			activate_plugin( 'classic-editor/classic-editor.php' );
61
		}
62
	}
63
64 View Code Duplication
	static function sort_requires_connection_last( $module1, $module2 ) {
65
		if ( $module1['requires_connection'] == $module2['requires_connection'] ) {
66
			return 0;
67
		} elseif ( $module1['requires_connection'] ) {
68
			return 1;
69
		} elseif ( $module2['requires_connection'] ) {
70
			return -1;
71
		}
72
73
		return 0;
74
	}
75
76
	// Produce JS understandable objects of modules containing information for
77
	// presentation like description, name, configuration url, etc.
78
	function get_modules() {
79
		include_once JETPACK__PLUGIN_DIR . 'modules/module-info.php';
80
		$available_modules = Jetpack::get_available_modules();
81
		$active_modules    = Jetpack::get_active_modules();
82
		$modules           = array();
83
		$jetpack_active    = Jetpack::is_active() || ( new Status() )->is_development_mode();
84
		$overrides         = Jetpack_Modules_Overrides::instance();
85
		foreach ( $available_modules as $module ) {
86
			if ( $module_array = Jetpack::get_module( $module ) ) {
87
				/**
88
				 * Filters each module's short description.
89
				 *
90
				 * @since 3.0.0
91
				 *
92
				 * @param string $module_array['description'] Module description.
93
				 * @param string $module Module slug.
94
				 */
95
				$short_desc = apply_filters( 'jetpack_short_module_description', $module_array['description'], $module );
96
				// Fix: correct multibyte strings truncate with checking for mbstring extension
97
				$short_desc_trunc = ( function_exists( 'mb_strlen' ) )
98
							? ( ( mb_strlen( $short_desc ) > 143 )
99
								? mb_substr( $short_desc, 0, 140 ) . '...'
100
								: $short_desc )
101
							: ( ( strlen( $short_desc ) > 143 )
102
								? substr( $short_desc, 0, 140 ) . '...'
103
								: $short_desc );
104
105
				$module_array['module']            = $module;
106
				$module_array['activated']         = ( $jetpack_active ? in_array( $module, $active_modules ) : false );
107
				$module_array['deactivate_nonce']  = wp_create_nonce( 'jetpack_deactivate-' . $module );
108
				$module_array['activate_nonce']    = wp_create_nonce( 'jetpack_activate-' . $module );
109
				$module_array['available']         = self::is_module_available( $module_array );
110
				$module_array['short_description'] = $short_desc_trunc;
111
				$module_array['configure_url']     = Jetpack::module_configuration_url( $module );
112
				$module_array['override']          = $overrides->get_module_override( $module );
113
114
				ob_start();
115
				/**
116
				 * Allow the display of a "Learn More" button.
117
				 * The dynamic part of the action, $module, is the module slug.
118
				 *
119
				 * @since 3.0.0
120
				 */
121
				do_action( 'jetpack_learn_more_button_' . $module );
122
				$module_array['learn_more_button'] = ob_get_clean();
123
124
				ob_start();
125
				/**
126
				 * Allow the display of information text when Jetpack is connected to WordPress.com.
127
				 * The dynamic part of the action, $module, is the module slug.
128
				 *
129
				 * @since 3.0.0
130
				 */
131
				do_action( 'jetpack_module_more_info_' . $module );
132
133
				/**
134
				* Filter the long description of a module.
135
				*
136
				* @since 3.5.0
137
				*
138
				* @param string ob_get_clean() The module long description.
139
				* @param string $module The module name.
140
				*/
141
				$module_array['long_description'] = apply_filters( 'jetpack_long_module_description', ob_get_clean(), $module );
142
143
				ob_start();
144
				/**
145
				 * Filter the search terms for a module
146
				 *
147
				 * Search terms are typically added to the module headers, under "Additional Search Queries".
148
				 *
149
				 * Use syntax:
150
				 * function jetpack_$module_search_terms( $terms ) {
151
				 *  $terms = _x( 'term 1, term 2', 'search terms', 'jetpack' );
152
				 *  return $terms;
153
				 * }
154
				 * add_filter( 'jetpack_search_terms_$module', 'jetpack_$module_search_terms' );
155
				 *
156
				 * @since 3.5.0
157
				 *
158
				 * @param string The search terms (comma separated).
159
				 */
160
				echo apply_filters( 'jetpack_search_terms_' . $module, $module_array['additional_search_queries'] );
161
				$module_array['search_terms'] = ob_get_clean();
162
163
				$module_array['configurable'] = false;
164
				if (
165
					current_user_can( 'manage_options' ) &&
166
					/**
167
					 * Allow the display of a configuration link in the Jetpack Settings screen.
168
					 *
169
					 * @since 3.0.0
170
					 *
171
					 * @param string $module Module name.
172
					 * @param bool false Should the Configure module link be displayed? Default to false.
173
					 */
174
					apply_filters( 'jetpack_module_configurable_' . $module, false )
175
				) {
176
					$module_array['configurable'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $module_array['configure_url'] ), __( 'Configure', 'jetpack' ) );
177
				}
178
179
				$modules[ $module ] = $module_array;
180
			}
181
		}
182
183
		uasort( $modules, array( 'Jetpack', 'sort_modules' ) );
184
185
		if ( ! Jetpack::is_active() ) {
186
			uasort( $modules, array( __CLASS__, 'sort_requires_connection_last' ) );
187
		}
188
189
		return $modules;
190
	}
191
192
	static function is_module_available( $module ) {
193
		if ( ! is_array( $module ) || empty( $module ) ) {
194
			return false;
195
		}
196
197
		/**
198
		 * We never want to show VaultPress as activatable through Jetpack.
199
		 */
200
		if ( 'vaultpress' === $module['module'] ) {
201
			return false;
202
		}
203
204
		/*
205
		 * WooCommerce Analytics should only be available
206
		 * when running WooCommerce 3+
207
		 */
208
		if (
209
			'woocommerce-analytics' === $module['module']
210
			&& (
211
				! class_exists( 'WooCommerce' )
212
				|| version_compare( WC_VERSION, '3.0', '<' )
213
			)
214
		) {
215
			return false;
216
		}
217
218
		if ( ( new Status() )->is_development_mode() ) {
219
			return ! ( $module['requires_connection'] );
220
		} else {
221
			if ( ! Jetpack::is_active() ) {
222
				return false;
223
			}
224
225
			return Jetpack_Plan::supports( $module['module'] );
226
		}
227
	}
228
229
	function handle_unrecognized_action( $action ) {
230
		switch ( $action ) {
231
			case 'bulk-activate':
232
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
233
					break;
234
				}
235
236
				$modules = (array) $_GET['modules'];
237
				$modules = array_map( 'sanitize_key', $modules );
238
				check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
239
				foreach ( $modules as $module ) {
240
					Jetpack::log( 'activate', $module );
241
					Jetpack::activate_module( $module, false );
242
				}
243
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
244
				wp_safe_redirect( wp_get_referer() );
245
				exit;
246 View Code Duplication
			case 'bulk-deactivate':
247
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
248
					break;
249
				}
250
251
				$modules = (array) $_GET['modules'];
252
				$modules = array_map( 'sanitize_key', $modules );
253
				check_admin_referer( 'bulk-jetpack_page_jetpack_modules' );
254
				foreach ( $modules as $module ) {
255
					Jetpack::log( 'deactivate', $module );
256
					Jetpack::deactivate_module( $module );
257
					Jetpack::state( 'message', 'module_deactivated' );
258
				}
259
				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...
260
				wp_safe_redirect( wp_get_referer() );
261
				exit;
262
			default:
263
				return;
264
		}
265
	}
266
267
	function fix_redirect( $module, $redirect = true ) {
268
		if ( ! $redirect ) {
269
			return;
270
		}
271
		if ( wp_get_referer() ) {
272
			add_filter( 'wp_redirect', 'wp_get_referer' );
273
		}
274
	}
275
276
	function admin_menu_debugger() {
277
		jetpack_require_lib( 'debugger' );
278
		Jetpack_Debugger::disconnect_and_redirect();
279
		$debugger_hook = add_submenu_page(
280
			null,
281
			__( 'Debugging Center', 'jetpack' ),
282
			'',
283
			'manage_options',
284
			'jetpack-debugger',
285
			array( $this, 'wrap_debugger_page' )
286
		);
287
		add_action( "admin_head-$debugger_hook", array( 'Jetpack_Debugger', 'jetpack_debug_admin_head' ) );
288
	}
289
290
	function wrap_debugger_page() {
291
		nocache_headers();
292
		if ( ! current_user_can( 'manage_options' ) ) {
293
			die( '-1' );
294
		}
295
		Jetpack_Admin_Page::wrap_ui( array( $this, 'debugger_page' ) );
296
	}
297
298
	function debugger_page() {
299
		jetpack_require_lib( 'debugger' );
300
		Jetpack_Debugger::jetpack_debug_display_handler();
301
	}
302
}
303
Jetpack_Admin::init();
304