Passed
Push — master ( ad1bda...a722cd )
by Chris
02:54
created

MonsterInsights_Onboarding_Wizard   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 460
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 235
dl 0
loc 460
rs 8.96
c 0
b 0
f 0
wmc 43

15 Methods

Rating   Name   Duplication   Size   Complexity  
A enqueue_scripts() 0 49 3
A onboarding_wizard_header() 0 11 1
B should_include_eu_addon() 0 32 8
B install_and_activate_wpforms() 0 81 6
A onboarding_wizard_footer() 0 4 1
A get_install_errors() 0 3 1
A change_success_url() 0 9 1
B maybe_load_onboarding_wizard() 0 15 7
A get_eu_countries() 0 55 1
A onboarding_wizard_content() 0 3 1
A __construct() 0 20 1
B is_code_installed_frontend() 0 37 8
A change_return_url() 0 21 2
A load_onboarding_wizard() 0 9 1
A add_dashboard_page() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like MonsterInsights_Onboarding_Wizard 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.

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

1
<?php
2
/**
3
 * MonsterInsights Onboarding Wizard
4
 *
5
 * @since 7.3
6
 *
7
 * @package MonsterInsights
8
 */
9
10
// Exit if accessed directly.
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * Class MonsterInsights_Dashboard_Widget
17
 */
18
class MonsterInsights_Onboarding_Wizard {
19
20
21
	/**
22
	 * MonsterInsights_Onboarding_Wizard constructor.
23
	 */
24
	public function __construct() {
25
26
		add_action( 'admin_init', array( $this, 'maybe_load_onboarding_wizard' ) );
27
28
		add_action( 'admin_menu', array( $this, 'add_dashboard_page' ) );
29
30
		add_action( 'wp_ajax_monsterinsights_onboarding_wpforms_install', array(
31
			$this,
32
			'install_and_activate_wpforms',
33
		) );
34
35
		add_action( 'wp_ajax_monsterinsights_onboarding_get_errors', array(
36
			$this,
37
			'get_install_errors',
38
		) );
39
40
		// This will only be called in the Onboarding Wizard context because of previous checks.
41
		add_filter( 'monsterinsights_maybe_authenticate_siteurl', array( $this, 'change_return_url' ) );
42
		add_filter( 'monsterinsights_auth_success_redirect_url', array( $this, 'change_success_url' ) );
43
		add_filter( 'monsterinsights_reauth_success_redirect_url', array( $this, 'change_success_url' ) );
44
45
	}
46
47
	/**
48
	 * Checks if the Wizard should be loaded in current context.
49
	 */
50
	public function maybe_load_onboarding_wizard() {
51
52
		// Check for wizard-specific parameter
53
		// Allow plugins to disable the onboarding wizard
54
		// Check if current user is allowed to save settings.
55
		if ( ! ( isset( $_GET['page'] ) || 'monsterinsights-onboarding' !== $_GET['page'] || apply_filters( 'monsterinsights_enable_onboarding_wizard', true ) || ! current_user_can( 'monsterinsights_save_settings' ) ) ) { // WPCS: CSRF ok, input var ok.
56
			return;
57
		}
58
59
		// Don't load the interface if doing an ajax call.
60
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
61
			return;
62
		}
63
64
		$this->load_onboarding_wizard();
65
66
	}
67
68
	/**
69
	 * Register page through WordPress's hooks.
70
	 */
71
	public function add_dashboard_page() {
72
		add_dashboard_page( '', '', 'monsterinsights_save_settings', 'monsterinsights-onboarding', '' );
73
	}
74
75
	/**
76
	 * Load the Onboarding Wizard template.
77
	 */
78
	private function load_onboarding_wizard() {
79
80
		$this->enqueue_scripts();
81
82
		$this->onboarding_wizard_header();
83
		$this->onboarding_wizard_content();
84
		$this->onboarding_wizard_footer();
85
86
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
87
88
	}
89
90
	/**
91
	 * Load the scripts needed for the Onboarding Wizard.
92
	 */
93
	public function enqueue_scripts() {
94
95
		global $wp_version;
96
		$version_path = monsterinsights_is_pro_version() ? 'pro' : 'lite';
97
		if ( ! defined( 'MONSTERINSIGHTS_LOCAL_WIZARD_JS_URL' ) ) {
98
			wp_enqueue_style( 'monsterinsights-vue-style-vendors', plugins_url( $version_path . '/assets/vue/css/chunk-vendors.css', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version() );
99
			wp_enqueue_style( 'monsterinsights-vue-style-common', plugins_url( $version_path . '/assets/vue/css/chunk-common.css', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version() );
100
			wp_enqueue_style( 'monsterinsights-vue-style', plugins_url( $version_path . '/assets/vue/css/wizard.css', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version() );
101
			wp_enqueue_script( 'monsterinsights-vue-vendors', plugins_url( $version_path . '/assets/vue/js/chunk-vendors.js', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version(), true );
102
			wp_enqueue_script( 'monsterinsights-vue-common', plugins_url( $version_path . '/assets/vue/js/chunk-common.js', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version(), true );
103
			wp_register_script( 'monsterinsights-vue-script', plugins_url( $version_path . '/assets/vue/js/wizard.js', MONSTERINSIGHTS_PLUGIN_FILE ), array(
104
				'monsterinsights-vue-vendors',
105
				'monsterinsights-vue-common',
106
			), monsterinsights_get_asset_version(), true );
107
		} else {
108
			wp_register_script( 'monsterinsights-vue-script', MONSTERINSIGHTS_LOCAL_WIZARD_JS_URL, array(), monsterinsights_get_asset_version(), true );
0 ignored issues
show
Bug introduced by
The constant MONSTERINSIGHTS_LOCAL_WIZARD_JS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
109
		}
110
		wp_enqueue_script( 'monsterinsights-vue-script' );
111
112
		wp_localize_script(
113
			'monsterinsights-vue-script',
114
			'monsterinsights',
115
			array(
116
				'ajax'                 => add_query_arg( 'page', 'monsterinsights-onboarding', admin_url( 'admin-ajax.php' ) ),
117
				'nonce'                => wp_create_nonce( 'mi-admin-nonce' ),
118
				'network'              => is_network_admin(),
119
				'translations'         => wp_get_jed_locale_data( 'mi-vue-app' ),
120
				'assets'               => plugins_url( $version_path . '/assets/vue', MONSTERINSIGHTS_PLUGIN_FILE ),
121
				'roles'                => monsterinsights_get_roles(),
122
				'roles_manage_options' => monsterinsights_get_manage_options_roles(),
123
				'wizard_url'           => admin_url( 'index.php?page=monsterinsights-onboarding' ),
124
				'is_eu'                => $this->should_include_eu_addon(),
125
				'activate_nonce'       => wp_create_nonce( 'monsterinsights-activate' ),
126
				'install_nonce'        => wp_create_nonce( 'monsterinsights-install' ),
127
				'exit_url'             => add_query_arg( 'page', 'monsterinsights_settings', admin_url( 'admin.php' ) ),
128
				'shareasale_id'        => monsterinsights_get_shareasale_id(),
129
				'shareasale_url'       => monsterinsights_get_shareasale_url( monsterinsights_get_shareasale_id(), '' ),
130
				// Used to add notices for future deprecations.
131
				'versions'             => array(
132
					'php_version'          => phpversion(),
133
					'php_version_below_54' => version_compare( phpversion(), '5.4', '<' ),
134
					'php_version_below_56' => version_compare( phpversion(), '5.6', '<' ),
135
					'php_update_link'      => monsterinsights_get_url( 'settings-notice', 'settings-page', 'https://www.monsterinsights.com/docs/update-php/' ),
136
					'wp_version'           => $wp_version,
137
					'wp_version_below_46'  => version_compare( $wp_version, '4.6', '<' ),
138
					'wp_version_below_49'  => version_compare( $wp_version, '4.9', '<' ),
139
					'wp_update_link'       => monsterinsights_get_url( 'settings-notice', 'settings-page', 'https://www.monsterinsights.com/docs/update-wordpress/' ),
140
				),
141
				'plugin_version'       => MONSTERINSIGHTS_VERSION,
142
			)
143
		);
144
145
	}
146
147
	/**
148
	 * Outputs the simplified header used for the Onboarding Wizard.
149
	 */
150
	public function onboarding_wizard_header() {
151
		?>
152
		<!DOCTYPE html>
153
		<html <?php language_attributes(); ?>>
154
		<head>
155
			<meta name="viewport" content="width=device-width"/>
156
			<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
157
			<title><?php esc_html_e( 'MonsterInsights &rsaquo; Onboarding Wizard', 'google-analytics-for-wordpress' ); ?></title>
158
			<?php do_action( 'admin_print_styles' ); ?>
159
			<?php do_action( 'admin_head' ); ?>
160
		</head>
161
		<body class="monsterinsights-onboarding">
162
		<?php
163
	}
164
165
	/**
166
	 * Outputs the content of the current step.
167
	 */
168
	public function onboarding_wizard_content() {
169
		?>
170
		<div id="monsterinsights-vue-onboarding-wizard"></div>
171
		<?php
172
	}
173
174
	/**
175
	 * Outputs the simplified footer used for the Onboarding Wizard.
176
	 */
177
	public function onboarding_wizard_footer() {
178
		?>
179
		<?php wp_print_scripts( 'monsterinsights-vue-script' ); ?>
180
		</body>
181
		</html>
182
		<?php
183
	}
184
185
	/**
186
	 * Check if we should suggest the EU Compliance addon by attempting to determine the website's location.
187
	 *
188
	 * @return bool
189
	 */
190
	public function should_include_eu_addon() {
191
192
		// Is WooCommerce installed and the countries class installed.
193
		if ( class_exists( 'WooCommerce' ) && class_exists( 'WC_Countries' ) ) {
194
			$wc_countries = new WC_Countries();
0 ignored issues
show
Bug introduced by
The type WC_Countries was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
195
			$country      = $wc_countries->get_base_country();
196
			$continent    = $wc_countries->get_continent_code_for_country( $country );
197
198
			if ( 'EU' === $continent ) {
199
				return true;
200
			}
201
		}
202
203
		// Is EDD installed?
204
		if ( class_exists( 'Easy_Digital_Downloads' ) && function_exists( 'edd_get_shop_country' ) ) {
205
			$country      = strtoupper( edd_get_shop_country() );
206
			$eu_countries = self::get_eu_countries();
207
208
			// Check if the country code is in the list of EU countries we have stored.
209
			if ( in_array( $country, $eu_countries, true ) ) {
210
				return true;
211
			}
212
		}
213
214
		// If no store installed, check the timezone setting.
215
		$timezone_string = get_option( 'timezone_string' );
216
		if ( 0 === strpos( strtolower( $timezone_string ), 'europe' ) ) {
0 ignored issues
show
Bug introduced by
It seems like $timezone_string can also be of type false; however, parameter $str of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

216
		if ( 0 === strpos( strtolower( /** @scrutinizer ignore-type */ $timezone_string ), 'europe' ) ) {
Loading history...
217
			// If the timezone is set to Europe, assume the website is based in Europe.
218
			return true;
219
		}
220
221
		return false;
222
223
	}
224
225
	/**
226
	 * Install WPForms lite and activate it, prevent initial setup step.
227
	 *
228
	 * @return null|string
229
	 */
230
	public function install_and_activate_wpforms() {
231
232
		check_ajax_referer( 'monsterinsights-install', 'nonce' );
233
234
		if ( ! current_user_can( 'install_plugins' ) ) {
235
			wp_send_json( array(
236
				'message' => esc_html__( 'You are not allowed to install plugins', 'google-analytics-for-wordpress' ),
237
			) );
238
		}
239
240
		include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
241
242
		$api = plugins_api( 'plugin_information', array(
243
			'slug'   => 'wpforms-lite',
244
			'fields' => array(
245
				'short_description' => false,
246
				'sections'          => false,
247
				'requires'          => false,
248
				'rating'            => false,
249
				'ratings'           => false,
250
				'downloaded'        => false,
251
				'last_updated'      => false,
252
				'added'             => false,
253
				'tags'              => false,
254
				'compatibility'     => false,
255
				'homepage'          => false,
256
				'donate_link'       => false,
257
			),
258
		) );
259
260
		if ( is_wp_error( $api ) ) {
261
			return $api->get_error_message();
262
		}
263
264
		$download_url = $api->download_link;
265
266
		$method = '';
267
		$url    = add_query_arg(
268
			array(
269
				'page' => 'monsterinsights-settings',
270
			),
271
			admin_url( 'admin.php' )
272
		);
273
		$url    = esc_url( $url );
274
275
		ob_start();
276
		if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, null ) ) ) {
277
			$form = ob_get_clean();
278
279
			wp_send_json( array( 'form' => $form ) );
280
		}
281
282
		// If we are not authenticated, make it happen now.
283
		if ( ! WP_Filesystem( $creds ) ) {
284
			ob_start();
285
			request_filesystem_credentials( $url, $method, true, false, null );
286
			$form = ob_get_clean();
287
288
			wp_send_json( array( 'form' => $form ) );
289
290
		}
291
292
		// We do not need any extra credentials if we have gotten this far, so let's install the plugin.
293
		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
294
		$base = MonsterInsights();
295
		require_once plugin_dir_path( $base->file ) . '/includes/admin/licensing/skin.php';
296
297
		// Create the plugin upgrader with our custom skin.
298
		$installer = new Plugin_Upgrader( new MonsterInsights_Skin() );
299
		$installer->install( $download_url );
300
301
		// Flush the cache and return the newly installed plugin basename.
302
		wp_cache_flush();
303
		if ( $installer->plugin_info() ) {
304
			// Set this option to prevent WP Forms setup from showing up after the wizard completes.
305
			update_option( 'wpforms_activation_redirect', true );
306
			activate_plugin( $installer->plugin_info() );
307
			wp_send_json_success();
308
		}
309
310
		wp_die();
311
312
	}
313
314
	/**
315
	 * Make a request to the front page and check if the tracking code is present.
316
	 *
317
	 * @return array
318
	 */
319
	public function is_code_installed_frontend() {
320
321
		// Grab the front page html.
322
		$request = wp_remote_request( home_url(), array(
323
			'sslverify' => false,
324
		) );
325
		$errors  = array();
326
327
		if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
0 ignored issues
show
Bug introduced by
It seems like $request can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_response_code() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

327
		if ( 200 === wp_remote_retrieve_response_code( /** @scrutinizer ignore-type */ $request ) ) {
Loading history...
328
329
			$body            = wp_remote_retrieve_body( $request );
0 ignored issues
show
Bug introduced by
It seems like $request can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_body() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

329
			$body            = wp_remote_retrieve_body( /** @scrutinizer ignore-type */ $request );
Loading history...
330
			$current_ua_code = monsterinsights_get_ua_to_output();
331
			// Translators: The placeholders are for making the "We noticed you're using a caching plugin" text bold.
332
			$cache_error = sprintf( esc_html__( '%1$sWe noticed you\'re using a caching plugin.%2$s Be sure to clear the cache to ensure the tracking appears on all pages and posts. %3$s(See this guide on how to clear cache)%4$s.', 'google-analytics-for-wordpress' ), '<b>', '</b>', ' <a href="https://www.wpbeginner.com/beginners-guide/how-to-clear-your-cache-in-wordpress/" target="_blank">', '</a>' );
333
			// Translators: The placeholders are for making the "We have detected multiple tracking codes" text bold & adding a link to support.
334
			$multiple_ua_error = sprintf( esc_html__( '%1$sWe have detected multiple tracking codes%2$s, you should remove non-MonsterInsights ones, if you need help finding them please %3$sread this article%4$s.', 'ga-premium' ), '<b>', '</b>', '<a href="https://www.monsterinsights.com/docs/how-to-find-duplicate-google-analytics-tracking-codes-in-wordpress/" target="_blank">', '</a>' );
335
336
			// First, check if the tracking frontend code is present.
337
			if ( false === strpos( $body, '__gaTracker' ) ) {
338
				$errors[] = $cache_error;
339
			} else {
340
				// Check if the current UA code is actually present.
341
				if ( $current_ua_code && false === strpos( $body, $current_ua_code ) ) {
342
					// We have the tracking code but using another UA, so it's cached.
343
					$errors[] = $cache_error;
344
				}
345
				// Grab all the UA codes from the page.
346
				$pattern = '/UA-[^\s\;\']+/m';
347
				preg_match_all( $pattern, $body, $matches );
348
				// If more than twice ( because MI has a ga-disable-UA also ), let them know to remove the others.
349
				if ( ! empty( $matches[0] ) && is_array( $matches[0] ) && count( $matches[0] ) > 2 ) {
350
					$errors[] = $multiple_ua_error;
351
				}
352
			}
353
		}
354
355
		return $errors;
356
357
	}
358
359
	/**
360
	 * Update the redirect url so the user returns to the Onboarding Wizard after auth.
361
	 *
362
	 * @param string $siteurl The url to which the user is redirected for auth.
363
	 *
364
	 * @return mixed
365
	 */
366
	public function change_return_url( $siteurl ) {
367
368
		$url = wp_parse_url( $siteurl );
369
370
		if ( isset( $url['query'] ) ) {
371
372
			parse_str( $url['query'], $parameters );
373
374
			$parameters['return'] = rawurlencode( add_query_arg( array(
375
				'page' => 'monsterinsights-onboarding',
376
			), admin_url() ) );
377
378
			$siteurl = str_replace( $url['query'], '', $siteurl );
379
380
			$siteurl = add_query_arg( $parameters, $siteurl );
381
382
			$siteurl .= '#/authenticate';
383
384
		}
385
386
		return $siteurl;
387
388
	}
389
390
	/**
391
	 * Update the success redirect URL so if all is well we get to the next step.
392
	 *
393
	 * @param string $siteurl The url to which the user is redirected after a successful auth.
394
	 *
395
	 * @return mixed
396
	 */
397
	public function change_success_url( $siteurl ) {
398
399
		$siteurl = add_query_arg( array(
400
			'page' => 'monsterinsights-onboarding',
401
		), admin_url() );
402
403
		$siteurl .= '#/recommended_settings';
404
405
		return $siteurl;
406
407
	}
408
409
	/**
410
	 * Retrieve an array of European countries.
411
	 *
412
	 * @return array
413
	 */
414
	public static function get_eu_countries() {
415
		return array(
416
			'AD',
417
			'AL',
418
			'AT',
419
			'AX',
420
			'BA',
421
			'BE',
422
			'BG',
423
			'BY',
424
			'CH',
425
			'CY',
426
			'CZ',
427
			'DE',
428
			'DK',
429
			'EE',
430
			'ES',
431
			'FI',
432
			'FO',
433
			'FR',
434
			'GB',
435
			'GG',
436
			'GI',
437
			'GR',
438
			'HR',
439
			'HU',
440
			'IE',
441
			'IM',
442
			'IS',
443
			'IT',
444
			'JE',
445
			'LI',
446
			'LT',
447
			'LU',
448
			'LV',
449
			'MC',
450
			'MD',
451
			'ME',
452
			'MK',
453
			'MT',
454
			'NL',
455
			'NO',
456
			'PL',
457
			'PT',
458
			'RO',
459
			'RS',
460
			'RU',
461
			'SE',
462
			'SI',
463
			'SJ',
464
			'SK',
465
			'SM',
466
			'TR',
467
			'UA',
468
			'VA',
469
		);
470
	}
471
472
	/**
473
	 * Ajax handler for grabbing the installed code status.
474
	 */
475
	public function get_install_errors() {
476
477
		wp_send_json( $this->is_code_installed_frontend() );
478
479
	}
480
481
}
482
483
new MonsterInsights_Onboarding_Wizard();
484