Passed
Branch master (29aa47)
by Chris
05:48
created

MonsterInsights_Install::v760_upgrades()   B

Complexity

Conditions 9
Paths 22

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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