Passed
Push — master ( 29aa47...a9ceae )
by Chris
09:18
created

MonsterInsights_Install::v700_upgrades()   B

Complexity

Conditions 9
Paths 54

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 12
nc 54
nop 0
dl 0
loc 30
rs 8.0555
c 0
b 0
f 0
1
<?php
2
/**
3
 * MonsterInsights Installation and Automatic Upgrades.
4
 *
5
 * This file handles setting up new
6
 * MonsterInsights installs as well as performing
7
 * behind the scene upgrades between
8
 * MonsterInsights versions.
9
 *
10
 * @package MonsterInsights
11
 * @subpackage Install/Upgrade
12
 * @since 6.0.0
13
 */
14
15
// Exit if accessed directly
16
if ( ! defined( 'ABSPATH' ) ) {
17
	exit;
18
}
19
20
/**
21
 * MonsterInsights Install.
22
 *
23
 * This class handles a new MI install
24
 * as well as automatic (non-user initiated)
25
 * upgrade routines.
26
 *
27
 * @since 6.0.0
28
 * @access public
29
 */
30
class MonsterInsights_Install {
31
32
	/**
33
	 * MI Settings.
34
	 *
35
	 * @since 6.0.0
36
	 * @access public
37
	 * @var array $new_settings When the init() function starts, initially
38
	 *      					contains the original settings. At the end
39
	 *      				 	of init() contains the settings to save.
40
	 */
41
	public $new_settings = array();
42
43
	/**
44
	 * Install/Upgrade routine.
45
	 *
46
	 * This function is what is called to actually install MI data on new installs and to do
47
	 * behind the scenes upgrades on MI upgrades. If this function contains a bug, the results
48
	 * can be catastrophic. This function gets the highest priority in all of MI for unit tests.
49
	 *
50
	 * @since 6.0.0
51
	 * @access public
52
	 *
53
	 * @return void
54
	 */
55
	public function init() {
56
57
		// Get a copy of the current MI settings.
58
		$this->new_settings = get_option( monsterinsights_get_option_name() );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_option(monsterinsights_get_option_name()) can also be of type false. However, the property $new_settings is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
59
60
		$version = get_option( 'monsterinsights_current_version', false );
61
		$cachec  = false; // have we forced an object cache to be cleared already (so we don't clear it unnecessarily)
62
63
		// if new install or Yoast Era instal
64
		if ( ! $version ) {
65
			// See if from Yoast
66
			$yoast   = get_option( 'yst_ga', false );
67
68
			// In case from Yoast, start from scratch
69
			delete_option( 'yoast-ga-access_token' );
70
			delete_option( 'yoast-ga-refresh_token' );
71
			delete_option( 'yst_ga' );
72
			delete_option( 'yst_ga_api' );
73
74
			$this->new_install();
75
76
			// set db version (Do not increment! See below large comment)
77
			update_option( 'monsterinsights_db_version', '7.4.0' );
78
79
			// Remove Yoast hook if present
80
			if ( wp_next_scheduled( 'yst_ga_aggregate_data' ) ) {
81
				wp_clear_scheduled_hook( 'yst_ga_aggregate_data' );
82
			}
83
84
			// Clear cache since coming from Yoast
85
			if ( ! $cachec && ! empty( $yoast ) ) {
0 ignored issues
show
introduced by
The condition $cachec is always false.
Loading history...
86
				wp_cache_flush();
87
				$cachec = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $cachec is dead and can be removed.
Loading history...
88
			}
89
		} else { // if existing install
90
			if ( version_compare( $version, '6.0.11', '<' ) ) {
91
				if ( ! $cachec ) {
0 ignored issues
show
introduced by
The condition $cachec is always false.
Loading history...
92
					wp_cache_flush();
93
					$cachec = true;
94
				}
95
			}
96
97
			if ( version_compare( $version, '7.0.0', '<' ) ) {
98
				$this->v700_upgrades();
99
			}
100
101
			if ( version_compare( $version, '7.4.0', '<' ) ) {
102
				$this->v740_upgrades();
103
				// Do not increment! See below large comment
104
				update_option( 'monsterinsights_db_version', '7.4.0' );
105
			}
106
107
			if ( version_compare( $version, '7.5.0', '<' ) ) {
108
				$this->v750_upgrades();
109
			}
110
111
			if ( version_compare( $version, '7.6.0', '<' ) ) {
112
				$this->v760_upgrades();
113
			}
114
115
			if ( version_compare( $version, '7.7.1', '<' ) ) {
116
				$this->v771_upgrades();
117
			}
118
119
			if ( version_compare( $version, '7.8.0', '<' ) ) {
120
				$this->v780_upgrades();
121
			}
122
123
			// Do not use. See monsterinsights_after_install_routine comment below.
124
			do_action( 'monsterinsights_after_existing_upgrade_routine', $version );
125
			$version = get_option( 'monsterinsights_current_version', $version );
126
			update_option( 'monsterinsights_version_upgraded_from', $version );
127
		}
128
129
		// This hook is used primarily by the Pro version to run some Pro
130
		// specific install stuff. Please do not use this hook. It is not
131
		// considered a public hook by MI's dev team and can/will be removed,
132
		// relocated, and/or altered without warning at any time. You've been warned.
133
		// As this hook is not for public use, we've intentionally not docbloc'd this
134
		// hook to avoid developers seeing it future public dev docs.
135
		do_action( 'monsterinsights_after_install_routine', $version );
136
137
		// This is the version of MI installed
138
		update_option( 'monsterinsights_current_version', MONSTERINSIGHTS_VERSION );
139
140
		// This is where we save MI settings
141
		update_option( monsterinsights_get_option_name(), $this->new_settings );
142
143
		// There's no code for this function below this. Just an explanation
144
		// of the MI core options.
145
146
		/**
147
		 * Explanation of MonsterInsights core options
148
		 *
149
		 * By now your head is probably spinning trying to figure
150
		 * out what all of these version options are for. Note, I've abbreviated
151
		 * "monsterinsights" to "mi" in the options names to make this table easier
152
		 * to read.
153
		 *
154
		 * Here's a basic rundown:
155
		 *
156
		 * mi_current_version:  This starts with the actual version MI was
157
		 * 						installed on. We use this version to
158
		 * 						determine whether or not a site needs
159
		 * 						to run one of the behind the scenes
160
		 * 						MI upgrade routines. This version is updated
161
		 * 						every time a minor or major background upgrade
162
		 * 						routine is run. Generally lags behind the
163
		 * 						MONSTERINSIGHTS_VERSION constant by at most a couple minor
164
		 * 						versions. Never lags behind by 1 major version
165
		 * 						or more generally.
166
		 *
167
		 * mi_db_version: 		This is different from mi_current_version.
168
		 * 						Unlike the former, this is used to determine
169
		 * 						if a site needs to run a *user* initiated
170
		 * 						upgrade routine (incremented in MI_Upgrade class). This
171
		 * 						value is only update when a user initiated
172
		 * 						upgrade routine is done. Because we do very
173
		 * 						few user initiated upgrades compared to
174
		 * 						automatic ones, this version can lag behind by
175
		 * 						2 or even 3 major versions. Generally contains
176
		 * 						the current major version.
177
		 *
178
		 * mi_settings:		    Returned by monsterinsights_get_option_name(), this
179
		 * 						is actually "monsterinsights_settings" for both pro
180
		 * 						and lite version. However we use a helper function to
181
		 * 						retrieve the option name in case we ever decide down the
182
		 * 						road to maintain seperate options for the Lite and Pro versions.
183
		 * 					 	If you need to access MI's settings directly, (as opposed to our
184
		 * 					 	monsterinsights_get_option helper which uses the option name helper
185
		 * 					 	automatically), you should use this function to get the
186
		 * 					 	name of the option to retrieve.
187
		 *
188
		 * Therefore you should never increment mi_db_version in this file and always increment mi_current_version.
189
		 */
190
	}
191
192
193
	/**
194
	 * New MonsterInsights Install routine.
195
	 *
196
	 * This function installs all of the default
197
	 * things on new MI installs. Flight 5476 with
198
	 * non-stop service to a whole world of
199
	 * possibilities is now boarding.
200
	 *
201
	 * @since 6.0.0
202
	 * @access public
203
	 *
204
	 * @return void
205
	 */
206
	public function new_install() {
207
208
		// Add default settings values
209
		$this->new_settings = $this->get_monsterinsights_default_values();
210
211
		$this->maybe_import_thirstyaffiliates_options();
212
213
		$data = array(
214
			'installed_version' => MONSTERINSIGHTS_VERSION,
215
			'installed_date'    => time(),
216
			'installed_pro'     => monsterinsights_is_pro_version(),
217
		);
218
219
		update_option( 'monsterinsights_over_time', $data );
220
221
		// Let addons + MI Pro/Lite hook in here. @todo: doc as nonpublic
222
		do_action( 'monsterinsights_after_new_install_routine', MONSTERINSIGHTS_VERSION );
223
	}
224
225
	public function get_monsterinsights_default_values() {
226
		return array(
227
			'enable_affiliate_links'    => true,
228
			'affiliate_links'           => array(
229
				array(
230
					'path'  => '/go/',
231
					'label' => 'affiliate',
232
				),
233
				array(
234
					'path'  => '/recommend/',
235
					'label' => 'affiliate',
236
				)
237
			),
238
			'demographics'              => 1,
239
			'ignore_users'              => array( 'administrator' ),
240
			'dashboards_disabled'       => 0,
241
			'anonymize_ips'             => 0,
242
			'extensions_of_files'       => 'doc,exe,js,pdf,ppt,tgz,zip,xls',
243
			'subdomain_tracking'        => '',
244
			'link_attribution'          => true,
245
			'tag_links_in_rss'          => true,
246
			'allow_anchor'              => 0,
247
			'add_allow_linker'          => 0,
248
			'custom_code'               => '',
249
			'save_settings'             => array( 'administrator' ),
250
			'view_reports'              => array( 'administrator', 'editor' ),
251
			'events_mode'               => 'js',
252
			'tracking_mode'             => 'analytics',
253
		);
254
	}
255
256
	/**
257
	 * Check if ThirstyAffiliates plugin is installed and use the link prefix value in the affiliate settings.
258
	 *
259
	 * @return void
260
	 */
261
	public function maybe_import_thirstyaffiliates_options() {
262
263
		// Check if ThirstyAffiliates is installed.
264
		if ( ! function_exists( 'ThirstyAffiliates' ) ) {
265
			return;
266
		}
267
268
		$link_prefix = get_option( 'ta_link_prefix', 'recommends' );
269
270
		if ( $link_prefix === 'custom' ) {
271
			$link_prefix = get_option( 'ta_link_prefix_custom', 'recommends' );
272
		}
273
274
		if ( ! empty( $link_prefix ) ) {
275
276
			// Check if prefix exists.
277
			$prefix_set = false;
278
			foreach ( $this->new_settings['affiliate_links'] as $affiliate_link ) {
279
				if ( $link_prefix === trim( $affiliate_link['path'], '/' ) ) {
280
					$prefix_set = true;
281
					break;
282
				}
283
			}
284
285
			if ( ! $prefix_set ) {
286
				$this->new_settings['affiliate_links'][] = array(
287
					'path'  => '/' . $link_prefix . '/',
288
					'label' => 'affiliate',
289
				);
290
			}
291
		}
292
	}
293
294
	/**
295
	 * MonsterInsights Version 7.0 upgrades.
296
	 *
297
	 * This function does the
298
	 * upgrade routine from MonsterInsights 6.2->7.0.
299
	 *
300
	 * @since 7.0.0
301
	 * @access public
302
	 *
303
	 * @return void
304
	 */
305
	public function v700_upgrades() {
306
		// 1. Default all event tracking and tracking to GA + JS respectively
307
			// 3a Set tracking_mode to use analytics.js
308
			$this->new_settings['tracking_mode' ] = 'analytics';
309
310
311
			// 3b Set events mode to use JS if the events mode is not set explicitly to none
312
			if ( empty( $this->new_settings['events_mode' ] ) || $this->new_settings['events_mode' ] !== 'none' ) {
313
				$this->new_settings['events_mode' ] = 'js';
314
			}
315
316
		// 2. Migrate manual UA codes
317
			// 2a Manual UA has the lowest priority
318
			if ( ! empty( $this->new_settings['manual_ua_code' ] ) ) {
319
				// Set as manual UA code
320
				is_network_admin() ? update_site_option( 'monsterinsights_network_profile', array( 'manual' => $this->new_settings['manual_ua_code' ] ) ) : update_option( 'monsterinsights_site_profile', array( 'manual' => $this->new_settings['manual_ua_code' ] ) );
321
			}
322
323
			// 2b Then try the oAuth UA code
324
			if ( ! empty( $this->new_settings['analytics_profile_code' ] ) ) {
325
				// Set as manual UA code
326
				is_network_admin() ? update_site_option( 'monsterinsights_network_profile', array( 'manual' => $this->new_settings['analytics_profile_code' ] ) ) : update_option( 'monsterinsights_site_profile', array( 'manual' => $this->new_settings['analytics_profile_code' ] ) );
327
			}
328
329
		// 3. Migrate License keys
330
		if ( is_multisite() ) {
331
			$ms_license = get_site_option( 'monsterinsights_license', '' );
332
			if ( $ms_license ) {
333
				update_site_option( 'monsterinsights_network_license_updates', get_site_option( 'monsterinsights_license_updates', '' ) );
334
				update_site_option( 'monsterinsights_network_license', $ms_license );
335
			}
336
		}
337
	}
338
339
	/**
340
	 * Upgrade routine for the new settings panel, onboarding wizard, and the internal-as-outbound v2 settings system.
341
	 */
342
	public function v740_upgrades() {
343
344
		// 1. Settings Conversions:
345
			// Convert affiliate field to repeater format
346
			if ( ! empty( $this->new_settings['track_internal_as_outbound'] ) ) {
347
				$affiliate_old_paths = $this->new_settings['track_internal_as_outbound'];
348
				$affiliate_old_label = isset( $this->new_settings['track_internal_as_label'] ) ? $this->new_settings['track_internal_as_label'] : '';
349
350
				$new_paths = explode( ',', $affiliate_old_paths );
351
352
				$this->new_settings['affiliate_links'] = array();
353
				if ( ! empty( $new_paths ) ) {
354
					$this->new_settings['enable_affiliate_links'] = true;
355
					foreach ( $new_paths as $new_path ) {
356
						$this->new_settings['affiliate_links'][] = array(
357
							'path'  => $new_path,
358
							'label' => $affiliate_old_label,
359
						);
360
					}
361
				}
362
363
				$settings = array(
364
					'track_internal_as_outbound',
365
					'track_internal_as_label',
366
				);
367
				foreach ( $settings as $setting ) {
368
					if ( ! empty( $this->new_settings[ $setting ] ) ) {
369
						unset( $this->new_settings[ $setting ] );
370
					}
371
				}
372
			}
373
374
			// Update option to disable just reports or also the dashboard widget.
375
			if ( isset( $this->new_settings['dashboards_disabled'] ) && $this->new_settings['dashboards_disabled'] ) {
376
				$this->new_settings['dashboards_disabled'] = 'disabled';
377
			}
378
379
			$this->new_settings['tracking_mode'] = 'analytics';
380
			$this->new_settings['events_mode']   = 'js';
381
382
			// If opted in during allow_tracking era, move that over
383
			if ( ! empty( $this->new_settings['allow_tracking'] ) ) {
384
				$this->new_settings['anonymous_data'] = 1;
385
			}
386
387
		// 2. Remove Yoast stuff
388
			delete_option( 'yoast-ga-access_token' );
389
			delete_option( 'yoast-ga-refresh_token' );
390
			delete_option( 'yst_ga' );
391
			delete_option( 'yst_ga_api' );
392
393
394
		// 3. Remove fake settings from other plugins using our key for some reason and old settings of ours
395
			$settings = array(
396
				'debug_mode',
397
				'track_download_as',
398
				'analytics_profile',
399
				'analytics_profile_code',
400
				'analytics_profile_name',
401
				'manual_ua_code',
402
				'track_outbound',
403
				'track_download_as',
404
				'enhanced_link_attribution',
405
				'oauth_version',
406
				'monsterinsights_oauth_status',
407
				'firebug_lite',
408
				'google_auth_code',
409
				'allow_tracking',
410
			);
411
412
			foreach ( $settings as $setting ) {
413
				if ( ! empty( $this->new_settings[ $setting ] ) ) {
414
					unset( $this->new_settings[ $setting ] );
415
				}
416
			}
417
418
			$settings = array(
419
				'_repeated',
420
				'ajax',
421
				'asmselect0',
422
				'bawac_force_nonce',
423
				'icl_post_language',
424
				'saved_values',
425
				'mlcf_email',
426
				'mlcf_name',
427
				'cron_failed',
428
				'undefined',
429
				'cf_email',
430
				'cf_message',
431
				'cf_name',
432
				'cf_number',
433
				'cf_phone',
434
				'cf_subject',
435
				'content',
436
				'credentials',
437
				'cron_failed',
438
				'cron_last_run',
439
				'global-css',
440
				'grids',
441
				'page',
442
				'punch-fonts',
443
				'return_tab',
444
				'skins',
445
				'navigation-skins',
446
				'title',
447
				'type',
448
				'wpcf_email',
449
				'wpcf_your_name',
450
			);
451
452
			foreach ( $settings as $setting ) {
453
				if ( ! empty( $this->new_settings[ $setting ] ) ) {
454
					unset( $this->new_settings[ $setting ] );
455
				}
456
			}
457
458
		// 4. Remove old crons
459
			if ( wp_next_scheduled( 'monsterinsights_daily_cron' ) ) {
460
				wp_clear_scheduled_hook( 'monsterinsights_daily_cron' );
461
			}
462
			if ( wp_next_scheduled( 'monsterinsights_send_tracking_data' ) ) {
463
				wp_clear_scheduled_hook( 'monsterinsights_send_tracking_data' );
464
			}
465
466
			if ( wp_next_scheduled( 'monsterinsights_send_tracking_checkin' ) ) {
467
				wp_clear_scheduled_hook( 'monsterinsights_send_tracking_checkin' );
468
			}
469
470
			if ( wp_next_scheduled( 'monsterinsights_weekly_cron' ) ) {
471
				wp_clear_scheduled_hook( 'monsterinsights_weekly_cron' );
472
			}
473
474
			if ( wp_next_scheduled( 'yst_ga_aggregate_data' ) ) {
475
				wp_clear_scheduled_hook( 'yst_ga_aggregate_data' );
476
			}
477
478
			delete_option( 'monsterinsights_tracking_last_send' );
479
			delete_option( 'mi_tracking_last_send' );
480
481
		// 5. Remove old option
482
			delete_option( 'monsterinsights_settings_version' );
483
	}
484
485
486
	/**
487
	 * Upgrade routine
488
	 */
489
	public function v750_upgrades() {
490
		// 1. One time re-prompt for anonymous data (due to migration bug now fixed)
491
		// if ( ! monsterinsights_is_pro_version() ) {
492
		// 	if ( empty( $this->new_settings[ 'anonymous_data' ] ) ) {
493
		// 		update_option( 'monsterinsights_tracking_notice', 0 );
494
		// 	}
495
		// }
496
		//
497
		// 2. Clear old settings ( 	'tracking_mode','events_mode',)
498
499
500
		// 3. Attempt to extract the cross-domain settings from the Custom Code area and use in the new option.
501
		$custom_code = isset( $this->new_settings['custom_code'] ) ? $this->new_settings['custom_code'] : '';
502
		if ( ! empty( $custom_code ) ) {
503
			$pattern = '/(?:\'linker:autoLink\', )(?:\[)(.*)(?:\])/m';
504
			preg_match_all( $pattern, $custom_code, $matches, PREG_SET_ORDER, 0 );
505
			if ( ! empty( $matches ) && isset( $matches[0] ) && isset( $matches[0][1] ) ) {
506
				$cross_domains = array();
507
				$domains       = explode( ',', $matches[0][1] );
508
				foreach ( $domains as $key => $domain ) {
509
					$domain          = trim( $domain );
510
					$cross_domains[] = array(
511
						'domain' => trim( $domain, '\'\"' ),
512
					);
513
				}
514
				$this->new_settings['add_allow_linker'] = true;
515
				$this->new_settings['cross_domains']    = $cross_domains;
516
517
				$notices = get_option( 'monsterinsights_notices' );
518
				if ( ! is_array( $notices ) ) {
519
					$notices = array();
520
				}
521
				$notices['monsterinsights_cross_domains_extracted'] = false;
522
				update_option( 'monsterinsights_notices', $notices );
523
			}
524
		}
525
	}
526
527
	/**
528
	 * Upgrade routine for version 7.6.0
529
	 */
530
	public function v760_upgrades() {
531
532
		$cross_domains = isset( $this->new_settings['cross_domains'] ) ? $this->new_settings['cross_domains'] : array();
533
534
		if ( ! empty( $cross_domains ) && is_array( $cross_domains ) ) {
535
			$current_domain = wp_parse_url( home_url() );
536
			$current_domain = isset( $current_domain['host'] ) ? $current_domain['host'] : '';
537
			if ( ! empty( $current_domain ) ) {
538
				$regex = '/^(?:' . $current_domain . '|(?:.+)\.' . $current_domain . ')$/m';
539
				foreach ( $cross_domains as $key => $cross_domain ) {
540
					if ( ! isset( $cross_domain['domain'] ) ) {
541
						continue;
542
					}
543
					preg_match( $regex, $cross_domain['domain'], $matches );
544
					if ( count( $matches ) > 0 ) {
545
						unset( $this->new_settings['cross_domains'][ $key ] );
546
					}
547
				}
548
			}
549
		}
550
551
	}
552
553
	/**
554
	 * Upgrade routine for version 7.7.1
555
	 */
556
	public function v771_upgrades() {
557
558
		if ( ! monsterinsights_is_pro_version() ) {
559
			// We only need to run this for the Pro version.
560
			return;
561
		}
562
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
563
564
		$plugin = 'wp-scroll-depth/wp-scroll-depth.php';
565
		// Check if wp-scroll-depth is active and deactivate to avoid conflicts with the pro scroll tracking feature.
566
		if ( is_plugin_active( $plugin ) ) {
567
			deactivate_plugins( $plugin );
568
		}
569
570
	}
571
572
	/**
573
	 * Upgrade routine for version 7.7.2
574
	 */
575
	public function v780_upgrades() {
576
577
		if ( monsterinsights_get_ua() ) {
578
			// If we have a UA, don't show the first run notice.
579
			monsterinsights_update_option( 'monsterinsights_first_run_notice', true );
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of monsterinsights_update_option(). ( Ignorable by Annotation )

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

579
			monsterinsights_update_option( 'monsterinsights_first_run_notice', /** @scrutinizer ignore-type */ true );
Loading history...
580
581
			// If they are already tracking when they upgrade, mark connected time as now.
582
			$over_time = get_option( 'monsterinsights_over_time', array() );
583
			if ( empty( $over_time['connected_date'] ) ) {
584
				$over_time['connected_date'] = time();
585
				update_option( 'monsterinsights_over_time', $over_time );
586
			}
587
		}
588
589
	}
590
}
591