Completed
Push — update/hide-jetpack-admin-ui-i... ( 364d69 )
by
unknown
412:55 queued 403:20
created

Jetpack_React_Page   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 288
Duplicated Lines 4.51 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 13
loc 288
rs 8.5454
c 0
b 0
f 0
wmc 49
lcom 1
cbo 4

14 Methods

Rating   Name   Duplication   Size   Complexity  
A get_page_hook() 0 6 1
B jetpack_add_dashboard_sub_nav_item() 0 10 7
A jetpack_add_settings_sub_nav_item() 0 6 4
A add_fallback_head_meta() 0 3 1
A add_noscript_head_meta() 0 5 1
A add_legacy_browsers_head_script() 0 10 1
A jetpack_menu_order() 13 13 4
A render_nojs_configurable() 0 14 3
B add_page_actions() 0 27 5
B page_render() 0 27 5
A get_i18n_data() 0 14 4
A get_dismissed_jetpack_notices() 0 12 1
A page_admin_styles() 0 6 2
C page_admin_scripts() 0 93 10

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_React_Page 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_React_Page, and based on these observations, apply Extract Interface, too.

1
<?php
2
include_once( 'class.jetpack-admin-page.php' );
3
4
// Builds the landing page and its menu
5
class Jetpack_React_Page extends Jetpack_Admin_Page {
6
7
	protected $dont_show_if_not_active = false;
8
9
	protected $is_redirecting = false;
10
11
	function get_page_hook() {
12
		$title = _x( 'Jetpack', 'The menu item label', 'jetpack' );
13
14
		// Add the main admin Jetpack menu
15
		return add_menu_page( 'Jetpack', $title, 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div' );
16
	}
17
18
	function add_page_actions( $hook ) {
19
		/** This action is documented in class.jetpack.php */
20
		do_action( 'jetpack_admin_menu', $hook );
21
22
		// Place the Jetpack menu item on top and others in the order they appear
23
		add_filter( 'custom_menu_order',         '__return_true' );
24
		add_filter( 'menu_order',                array( $this, 'jetpack_menu_order' ) );
25
26
		if ( ! isset( $_GET['page'] ) || 'jetpack' !== $_GET['page'] || ! empty( $_GET['configure'] ) ) {
27
			return; // No need to handle the fallback redirection if we are not on the Jetpack page
28
		}
29
30
		// Adding a redirect meta tag for older WordPress versions
31
		if ( $this->is_wp_version_too_old() ) {
32
			$this->is_redirecting = true;
33
			add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) );
34
		}
35
36
		// Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled
37
		add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) );
38
39
		// Enqueue admin page styles in head
40
		add_action( 'admin_enqueue_scripts', array( $this, 'page_admin_styles' ) );
41
42
		// Adding a redirect tag wrapped in browser conditional comments
43
		add_action( 'admin_head', array( $this, 'add_legacy_browsers_head_script' ) );
44
	}
45
46
	/**
47
	 * Add Jetpack Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active.
48
	 * Otherwise and only if user is allowed to see the Jetpack Admin, the Dashboard sub-link is added but pointed to Apps tab.
49
	 *
50
	 * Works in Dev Mode or when user is connected.
51
	 *
52
	 * @since 4.3.0
53
	 */
54
	function jetpack_add_dashboard_sub_nav_item() {
55
		if ( Jetpack::is_development_mode() || Jetpack::is_active() ) {
56
			global $submenu;
57
			if ( current_user_can( 'jetpack_manage_modules' ) || Jetpack::is_module_active( 'protect' ) || current_user_can( 'view_stats' ) ) {
58
				$submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/dashboard' ) );
59
			} elseif ( current_user_can( 'jetpack_admin_page' ) ) {
60
				$submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/apps' ) );
61
			}
62
		}
63
	}
64
65
	/**
66
	 * If user is allowed to see the Jetpack Admin, add Settings sub-link.
67
	 *
68
	 * @since 4.3.0
69
	 */
70
	function jetpack_add_settings_sub_nav_item() {
71
		if ( ( Jetpack::is_development_mode() || Jetpack::is_active() ) && current_user_can( 'jetpack_admin_page' ) ) {
72
			global $submenu;
73
			$submenu['jetpack'][] = array( __( 'Settings', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/settings' ) );
74
		}
75
	}
76
77
	function add_fallback_head_meta() {
78
		echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
79
	}
80
81
	function add_noscript_head_meta() {
82
		echo '<noscript>';
83
		$this->add_fallback_head_meta();
84
		echo '</noscript>';
85
	}
86
87
	function add_legacy_browsers_head_script() {
88
		echo
89
			"<script type=\"text/javascript\">\n"
90
			. "/*@cc_on\n"
91
			. "if ( @_jscript_version <= 10) {\n"
92
			. "window.location.href = '?page=jetpack_modules';\n"
93
			. "}\n"
94
			. "@*/\n"
95
			. "</script>";
96
	}
97
98 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
99
		$jp_menu_order = array();
100
101
		foreach ( $menu_order as $index => $item ) {
102
			if ( $item != 'jetpack' )
103
				$jp_menu_order[] = $item;
104
105
			if ( $index == 0 )
106
				$jp_menu_order[] = 'jetpack';
107
		}
108
109
		return $jp_menu_order;
110
	}
111
112
	// Render the configuration page for the module if it exists and an error
113
	// screen if the module is not configurable
114
	// @todo remove when real settings are in place
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
115
	function render_nojs_configurable( $module_name ) {
0 ignored issues
show
Unused Code introduced by
The parameter $module_name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
116
		$module_name = preg_replace( '/[^\da-z\-]+/', '', $_GET['configure'] );
117
118
		include_once( JETPACK__PLUGIN_DIR . '_inc/header.php' );
119
		echo '<div class="wrap configure-module">';
120
121
		if ( Jetpack::is_module( $module_name ) && current_user_can( 'jetpack_configure_modules' ) ) {
122
			Jetpack::admin_screen_configure_module( $module_name );
123
		} else {
124
			echo '<h2>' . esc_html__( 'Error, bad module.', 'jetpack' ) . '</h2>';
125
		}
126
127
		echo '</div><!-- /wrap -->';
128
	}
129
130
	function page_render() {
131
		// We're in an IDC: we need a decision made before we show the UI again.
132
		if ( Jetpack::validate_sync_error_idc_option() && ! Jetpack_Options::get_option( 'safe_mode_confirmed' ) ) {
133
			return false;
134
		}
135
136
		// Handle redirects to configuration pages
137
		if ( ! empty( $_GET['configure'] ) ) {
138
			return $this->render_nojs_configurable( $_GET['configure'] );
139
		}
140
141
		/** This action is already documented in views/admin/admin-page.php */
142
		do_action( 'jetpack_notices' );
143
144
		// Try fetching by patch
145
		$static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' );
146
147
		if ( false === $static_html ) {
148
149
			// If we still have nothing, display an error
150
			esc_html_e( 'Error fetching static.html.', 'jetpack' );
151
		} else {
152
153
			// We got the static.html so let's display it
154
			echo $static_html;
155
		}
156
	}
157
158
	function get_i18n_data() {
159
160
		$i18n_json = JETPACK__PLUGIN_DIR . 'languages/json/jetpack-' . get_locale() . '.json';
161
162
		if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
163
			$locale_data = @file_get_contents( $i18n_json );
164
			if ( $locale_data ) {
165
				return $locale_data;
166
			}
167
		}
168
169
		// Return empty if we have nothing to return so it doesn't fail when parsed in JS
170
		return '{}';
171
	}
172
173
	/**
174
	 * Gets array of any Jetpack notices that have been dismissed.
175
	 *
176
	 * @since 4.0.1
177
	 * @return mixed|void
178
	 */
179
	function get_dismissed_jetpack_notices() {
180
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
181
		/**
182
		 * Array of notices that have been dismissed.
183
		 *
184
		 * @since 4.0.1
185
		 *
186
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
187
		 */
188
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
189
		return $dismissed_notices;
190
	}
191
192
	function page_admin_styles() {
193
		$rtl = is_rtl() ? '.rtl' : '';
194
195
		wp_enqueue_style( 'dops-css', plugins_url( "_inc/build/admin.dops-style$rtl.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
196
		wp_enqueue_style( 'components-css', plugins_url( "_inc/build/style.min$rtl.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
197
	}
198
199
	function page_admin_scripts() {
200
		if ( $this->is_redirecting ) {
201
			return; // No need for scripts on a fallback page
202
		}
203
204
		$is_dev_mode = Jetpack::is_development_mode();
205
206
		// Enqueue jp.js and localize it
207
		wp_enqueue_script( 'react-plugin', plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true );
208
209
		if ( ! $is_dev_mode ) {
210
			// Required for Analytics
211
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
212
		}
213
214
		$localeSlug = explode( '_', get_locale() );
215
		$localeSlug = $localeSlug[0];
216
217
		// Collecting roles that can view site stats
218
		$stats_roles = array();
219
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
220
		foreach( get_editable_roles() as $slug => $role ) {
221
			$stats_roles[ $slug ] = array(
222
				'name' => translate_user_role( $role['name'] ),
223
				'canView' => in_array( $slug, $enabled_roles, true ),
224
			);
225
		}
226
227
		$response = rest_do_request( new WP_REST_Request( 'GET', '/jetpack/v4/module/all' ) );
228
		$modules = $response->get_data();
229
230
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
231
		// respective characters.
232
		foreach( $modules as $slug => $data ) {
233
			$modules[ $slug ]['name'] = html_entity_decode( $data['name'] );
234
			$modules[ $slug ]['description'] = html_entity_decode( $data['description'] );
235
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
236
			$modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] );
237
		}
238
239
		// Add objects to be passed to the initial state of the app
240
		wp_localize_script( 'react-plugin', 'Initial_State', array(
241
			'WP_API_root' => esc_url_raw( rest_url() ),
242
			'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
243
			'pluginBaseUrl' => plugins_url( '', JETPACK__PLUGIN_FILE ),
244
			'connectionStatus' => array(
245
				'isActive'  => Jetpack::is_active(),
246
				'isStaging' => Jetpack::is_staging_site(),
247
				'devMode'   => array(
248
					'isActive' => $is_dev_mode,
249
					'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
250
					'url'      => site_url() && false === strpos( site_url(), '.' ),
251
					'filter'   => apply_filters( 'jetpack_development_mode', false ),
252
				),
253
				'isPublic'	=> '1' == get_option( 'blog_public' ),
254
				'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
255
			),
256
			'dismissedNotices' => $this->get_dismissed_jetpack_notices(),
257
			'isDevVersion' => Jetpack::is_development_version(),
258
			'currentVersion' => JETPACK__VERSION,
259
			'happinessGravIds' => jetpack_get_happiness_gravatar_ids(),
260
			'getModules' => $modules,
261
			'showJumpstart' => jetpack_show_jumpstart(),
262
			'rawUrl' => Jetpack::build_raw_urls( get_home_url() ),
263
			'adminUrl' => esc_url( admin_url() ),
264
			'stats' => array(
265
				// data is populated asynchronously on page load
266
				'data'  => array(
267
					'general' => false,
268
					'day'     => false,
269
					'week'    => false,
270
					'month'   => false,
271
				),
272
				'roles' => $stats_roles,
273
			),
274
			'settingNames' => array(
275
				'jetpack_holiday_snow_enabled' => function_exists( 'jetpack_holiday_snow_option_name' ) ? jetpack_holiday_snow_option_name() : false,
276
			),
277
			'userData' => array(
278
//				'othersLinked' => Jetpack::get_other_linked_admins(),
279
				'currentUser'  => jetpack_current_user_data(),
280
			),
281
			'locale' => $this->get_i18n_data(),
282
			'localeSlug' => $localeSlug,
283
			'jetpackStateNotices' => array(
284
				'messageCode' => Jetpack::state( 'message' ),
285
				'errorCode' => Jetpack::state( 'error' ),
286
				'errorDescription' => Jetpack::state( 'error_description' ),
287
			),
288
			'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
289
			'currentIp' => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false
290
		) );
291
	}
292
}
293
294
/*
295
 * List of happiness Gravatar IDs
296
 *
297
 * @todo move to functions.global.php when available
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
298
 * @since 4.1.0
299
 * @return array
300
 */
301
function jetpack_get_happiness_gravatar_ids() {
302
	return array(
303
		'623f42e878dbd146ddb30ebfafa1375b',
304
		'561be467af56cefa58e02782b7ac7510',
305
		'd8ad409290a6ae7b60f128a0b9a0c1c5',
306
		'790618302648bd80fa8a55497dfd8ac8',
307
		'6e238edcb0664c975ccb9e8e80abb307',
308
		'4e6c84eeab0a1338838a9a1e84629c1a',
309
		'9d4b77080c699629e846d3637b3a661c',
310
		'4626de7797aada973c1fb22dfe0e5109',
311
		'190cf13c9cd358521085af13615382d5',
312
		'f7006d10e9f7dd7bea89a001a2a2fd59',
313
		'16acbc88e7aa65104ed289d736cb9698',
314
		'4d5ad4219c6f676ea1e7d40d2e8860e8',
315
		'e301f7d01b09e7578fdfc1b1ec1bc08d',
316
		'42f4c73f5337486e199f6e3b3910f168',
317
		'e7b26de48e76498cff880abca1eed8da',
318
		'764fb02aaae2ff64c0625c763d82b74e',
319
		'4988305772319fb9bc8fce0a7acb3aa1',
320
		'5d8695c4b81592f1255721d2644627ca',
321
		'0e2249a7de3404bc6d5207a45e911187',
322
	);
323
}
324
325
/*
326
 * Only show Jump Start on first activation.
327
 * Any option 'jumpstart' other than 'new connection' will hide it.
328
 *
329
 * The option can be of 4 things, and will be stored as such:
330
 * new_connection      : Brand new connection - Show
331
 * jumpstart_activated : Jump Start has been activated - dismiss
332
 * jetpack_action_taken: Manual activation of a module already happened - dismiss
333
 * jumpstart_dismissed : Manual dismissal of Jump Start - dismiss
334
 *
335
 * @todo move to functions.global.php when available
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
336
 * @since 3.6
337
 * @return bool | show or hide
338
 */
339
function jetpack_show_jumpstart() {
340
	if ( ! Jetpack::is_active() ) {
341
		return false;
342
	}
343
	$jumpstart_option = Jetpack_Options::get_option( 'jumpstart' );
344
345
	$hide_options = array(
346
		'jumpstart_activated',
347
		'jetpack_action_taken',
348
		'jumpstart_dismissed'
349
	);
350
351
	if ( ! $jumpstart_option || in_array( $jumpstart_option, $hide_options ) ) {
352
		return false;
353
	}
354
355
	return true;
356
}
357
358
/*
359
 * Gather data about the master user.
360
 *
361
 * @since 4.1.0
362
 *
363
 * @return array
364
 */
365 View Code Duplication
function jetpack_master_user_data() {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
366
	$masterID = Jetpack_Options::get_option( 'master_user' );
367
	if ( ! get_user_by( 'id', $masterID ) ) {
368
		return false;
369
	}
370
371
	$jetpack_user = get_userdata( $masterID );
372
	$wpcom_user   = Jetpack::get_connected_user_data( $jetpack_user->ID );
373
	$gravatar     = get_avatar( $jetpack_user->ID, 40 );
374
375
	$master_user_data = array(
376
		'jetpackUser' => $jetpack_user,
377
		'wpcomUser'   => $wpcom_user,
378
		'gravatar'    => $gravatar,
379
	);
380
381
	return $master_user_data;
382
}
383
384
/**
385
 * Gather data about the current user.
386
 *
387
 * @since 4.1.0
388
 *
389
 * @return array
390
 */
391
function jetpack_current_user_data() {
392
	global $current_user;
393
	$is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
394
	$dotcom_data    = Jetpack::get_connected_user_data();
395
	// Add connected user gravatar to the returned dotcom_data
396
	$avatar_data = Jetpack::get_avatar_url( $dotcom_data[ 'email' ] );
397
	$dotcom_data[ 'avatar'] = $avatar_data[ 0 ];
398
399
	$current_user_data = array(
400
		'isConnected' => Jetpack::is_user_connected( $current_user->ID ),
401
		'isMaster'    => $is_master_user,
402
		'username'    => $current_user->user_login,
403
		'wpcomUser'   => $dotcom_data,
404
		'gravatar'    => get_avatar( $current_user->ID, 40 ),
405
		'permissions' => array(
406
			'admin_page'         => current_user_can( 'jetpack_admin_page' ),
407
			'connect'            => current_user_can( 'jetpack_connect' ),
408
			'disconnect'         => current_user_can( 'jetpack_disconnect' ),
409
			'manage_modules'     => current_user_can( 'jetpack_manage_modules' ),
410
			'network_admin'      => current_user_can( 'jetpack_network_admin_page' ),
411
			'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
412
			'edit_posts'         => current_user_can( 'edit_posts' ),
413
			'manage_options'     => current_user_can( 'manage_options' ),
414
			'view_stats'		 => current_user_can( 'view_stats' ),
415
			'manage_plugins'	 => current_user_can( 'install_plugins' )
416
									&& current_user_can( 'activate_plugins' )
417
									&& current_user_can( 'update_plugins' )
418
									&& current_user_can( 'delete_plugins' ),
419
		),
420
	);
421
422
	return $current_user_data;
423
}
424