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

MonsterInsights_License_Actions::admin_init()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 4
nop 0
dl 0
loc 18
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * License Actions class.
4
 *
5
 * @since 6.0.0
6
 *
7
 * @package MonsterInsights
8
 * @author  Chris Christoff
9
 */
10
11
// Exit if accessed directly
12
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
final class MonsterInsights_License_Actions {
17
	/**
18
	 * Holds the license key.
19
	 *
20
	 * @since 6.0.0
21
	 *
22
	 * @var string
23
	 */
24
	public $key;
25
26
	/**
27
	 * Holds any license error messages.
28
	 *
29
	 * @since 6.0.0
30
	 *
31
	 * @var array
32
	 */
33
	public $errors = array();
34
35
	/**
36
	 * Holds any license success messages.
37
	 *
38
	 * @since 6.0.0
39
	 *
40
	 * @var array
41
	 */
42
	public $success = array();
43
44
	/**
45
	 * Primary class constructor.
46
	 *
47
	 * @since 6.0.0
48
	 */
49
	public function __construct() {
50
		add_action( 'admin_init', array( $this, 'admin_init' ) );
51
52
		add_action( 'wp_ajax_monsterinsights_verify_license', array( $this, 'ajax_verify_key' ) );
53
54
		add_action( 'wp_ajax_monsterinsights_deactivate_license', array( $this, 'ajax_deactivate_key' ) );
55
56
		add_action( 'wp_ajax_monsterinsights_validate_license', array( $this, 'ajax_validate_key' ) );
57
	}
58
59
	public function admin_init() {
60
		// Possibly verify the key.
61
		$this->maybe_verify_key();
62
63
		// Add potential admin notices for actions around the admin.
64
		add_action( 'admin_notices', array( $this, 'monsterinsights_notices' ), 11 );
65
		add_action( 'network_admin_notices', array( $this, 'monsterinsights_notices' ), 11 );
66
67
		// Grab the license key. If it is not set (even after verification), return early.
68
		$this->key = is_network_admin() ? MonsterInsights()->license->get_network_license_key() : MonsterInsights()->license->get_site_license_key();
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
69
		if ( ! $this->key ) {
70
			return;
71
		}
72
73
		// Possibly handle validating, deactivating and refreshing license keys.
74
		$this->maybe_validate_key();
75
		$this->maybe_deactivate_key();
76
		$this->maybe_refresh_key();
77
	}
78
79
	/**
80
	 * Maybe verifies a license key entered by the user.
81
	 *
82
	 * @since 6.0.0
83
	 *
84
	 * @return null Return early if the key fails to be verified.
85
	 */
86
	public function maybe_verify_key() {
87
88
		if ( empty( $_POST['monsterinsights-license-key'] ) || strlen( $_POST['monsterinsights-license-key'] ) < 10 || empty( $_POST['monsterinsights-verify-submit'] ) ) {
89
			return;
90
		}
91
92
		if ( ! wp_verify_nonce( $_POST['monsterinsights-key-nonce'], 'monsterinsights-key-nonce' ) ) {
93
			return;
94
		}
95
96
		if ( ! current_user_can( 'monsterinsights_save_settings' ) ) {
97
			return;
98
		}
99
100
		$this->verify_key();
101
102
	}
103
104
	/**
105
	 * Ajax handler for verifying the License key.
106
	 */
107
	public function ajax_verify_key() {
108
109
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
110
111
		$network_admin = isset( $_POST['network'] ) && $_POST['network'];
112
		$license = ! empty( $_POST['license'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['license'] ) ) ) : false;
113
114
		if ( ! $license ) {
115
			wp_send_json_error(
116
				array(
117
					'error' => esc_html__( 'Please enter a license key.', 'google-analytics-for-wordpress' ),
118
				)
119
			);
120
		}
121
122
		if ( ! current_user_can( 'monsterinsights_save_settings' ) ) {
123
			wp_send_json_error(
124
				array(
125
					'error' => esc_html__( 'You are not allowed to verify a license key.', 'google-analytics-for-wordpress' ),
126
				)
127
			);
128
		}
129
130
		$forced = isset( $_POST['forced'] ) && 'true' === sanitize_text_field( wp_unslash( $_POST['forced'] ) );
0 ignored issues
show
Unused Code introduced by
The assignment to $forced is dead and can be removed.
Loading history...
131
132
		$verify = $this->perform_remote_request( 'verify-key', array( 'tgm-updater-key' => $license ) );
133
134
		if ( ! $verify ) {
135
			wp_send_json_error(
136
				array(
137
					'error' => esc_html__( 'There was an error connecting to the remote key API. Please try again later.', 'google-analytics-for-wordpress' ),
138
				)
139
			);
140
		}
141
142
		if ( ! empty( $verify->error ) ) {
143
			wp_send_json_error(
144
				array(
145
					'error' => $verify->error,
146
				)
147
			);
148
149
		}
150
151
		// Otherwise, our request has been done successfully. Update the option and set the success message.
152
		$option                = $network_admin ? MonsterInsights()->license->get_network_license() : MonsterInsights()->license->get_site_license();
153
		$option['key']         = $license;
154
		$option['type']        = isset( $verify->type ) ? $verify->type : $option['type'];
155
		$option['is_expired']  = false;
156
		$option['is_disabled'] = false;
157
		$option['is_invalid']  = false;
158
159
		$network_admin ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
160
		delete_transient( '_monsterinsights_addons' );
161
		monsterinsights_get_addons_data( $option['key'] );
162
163
		$message = isset( $verify->success ) ? $verify->success : esc_html__( 'Congratulations! This site is now receiving automatic updates.', 'google-analytics-for-wordpress' );
164
165
		wp_send_json_success(
166
			array(
167
				'message'      => $message,
168
				'license_type' => $option['type'],
169
			)
170
		);
171
172
	}
173
174
	/**
175
	 * Verifies a license key entered by the user.
176
	 *
177
	 * @since 6.0.0
178
	 */
179
	public function verify_key() {
180
181
		// Perform a request to verify the key.
182
		$verify = $this->perform_remote_request( 'verify-key', array( 'tgm-updater-key' => trim( $_POST['monsterinsights-license-key'] ) ) );
183
184
		// If it returns false, send back a generic error message and return.
185
		if ( ! $verify ) {
186
			$this->errors[] = esc_html__( 'There was an error connecting to the remote key API. Please try again later.', 'google-analytics-for-wordpress' );
187
188
			return;
189
		}
190
191
		// If an error is returned, set the error and return.
192
		if ( ! empty( $verify->error ) ) {
0 ignored issues
show
Bug introduced by
The property error does not exist on string|true.
Loading history...
193
			$this->errors[] = $verify->error;
194
195
			return;
196
		}
197
198
        // Otherwise, our request has been done successfully. Update the option and set the success message.
199
        $option                = is_network_admin() ? MonsterInsights()->license->get_network_license() : MonsterInsights()->license->get_site_license();
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
200
        $option['key']         = trim( $_POST['monsterinsights-license-key'] );
201
        $option['type']        = isset( $verify->type ) ? $verify->type : $option['type'];
0 ignored issues
show
Bug introduced by
The property type does not exist on string|true.
Loading history...
202
        $option['is_expired']  = false;
203
        $option['is_disabled'] = false;
204
        $option['is_invalid']  = false;
205
        $this->success[]       = isset( $verify->success ) ? $verify->success : esc_html__( 'Congratulations! This site is now receiving automatic updates.', 'google-analytics-for-wordpress' );
0 ignored issues
show
Bug introduced by
The property success does not exist on string|true.
Loading history...
206
        is_network_admin() ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
207
        delete_transient( '_monsterinsights_addons' );
208
        monsterinsights_get_addons_data( $option['key'] );
209
        // Make sure users can now update their plugins if they previously an expired key.
210
	    wp_clean_plugins_cache( true );
211
    }
212
213
	/**
214
	 * Maybe validates a license key entered by the user.
215
	 *
216
	 * @since 6.0.0
217
	 *
218
	 * @return null Return early if the transient has not expired yet.
219
	 */
220
	public function maybe_validate_key() {
221
		$check = is_network_admin() ? MonsterInsights()->license->time_to_check_network_license() : MonsterInsights()->license->time_to_check_site_license();
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
222
		if ( $check ) {
223
			$this->validate_key();
224
		}
225
	}
226
227
	/**
228
	 * Ajax handler for validating the current key.
229
	 */
230
	public function ajax_validate_key() {
231
232
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
233
234
		$this->validate_key( true );
235
236
		if ( ! empty( $this->errors ) ) {
237
			wp_send_json_error( array(
238
				'error' => $this->errors[0],
239
			) );
240
		}
241
242
		if ( ! empty( $this->success ) ) {
243
			wp_send_json_success(
244
				array(
245
					'message' => $this->success[0],
246
				)
247
			);
248
		}
249
250
		wp_die();
251
252
	}
253
254
	/**
255
	 * Validates a license key entered by the user.
256
	 *
257
	 * @since 6.0.0
258
	 *
259
	 * @param bool $forced Force to set contextual messages (false by default).
260
	 */
261
	public function validate_key( $forced = false, $network = null ) {
262
263
		if ( is_null( $network ) ) {
264
			$network = is_network_admin();
265
		}
266
267
		$validate = $this->perform_remote_request( 'validate-key', array( 'tgm-updater-key' => $this->key ) );
268
269
		// If there was a basic API error in validation, only set the transient for 10 minutes before retrying.
270
		if ( ! $validate ) {
271
			// If forced, set contextual success message.
272
			if ( $forced ) {
273
				$this->errors[] = esc_html__( 'There was an error connecting to the remote key API. Please try again later.', 'google-analytics-for-wordpress' );
274
			}
275
276
			return;
277
		}
278
279
		$option                = $network ? MonsterInsights()->license->get_network_license() : MonsterInsights()->license->get_site_license();
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
280
		$option['is_expired']  = false;
281
		$option['is_disabled'] = false;
282
		$option['is_invalid']  = false;
283
284
		// If a key or author error is returned, the license no longer exists or the user has been deleted, so reset license.
285
		if ( isset( $validate->key ) || isset( $validate->author ) ) {
0 ignored issues
show
Bug introduced by
The property key does not exist on string|true.
Loading history...
Bug introduced by
The property author does not exist on string|true.
Loading history...
286
			$option['is_invalid'] = true;
287
			$network ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
288
			$this->errors[] = esc_html__( 'Your license key for MonsterInsights is invalid. The key no longer exists or the user associated with the key has been deleted. Please use a different key.', 'google-analytics-for-wordpress' );
289
290
			return;
291
		}
292
293
		// If the license has expired, set the transient and expired flag and return.
294
		if ( isset( $validate->expired ) ) {
0 ignored issues
show
Bug introduced by
The property expired does not exist on string|true.
Loading history...
295
			$option['is_expired'] = true;
296
			$network ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
297
			// Translators: The link to the MonsterInsights website.
298
			$this->errors[] = sprintf( esc_html__( 'Your license key for MonsterInsights has expired. %1$sPlease click here to renew your license key.%2$s', 'google-analytics-for-wordpress' ), '<a href="' . monsterinsights_get_url( 'admin-notices', 'expired-license', 'https://www.monsterinsights.com/login/' ) . '" target="_blank" rel="noopener noreferrer" referrer="no-referrer">', '</a>' );
299
300
			return;
301
		}
302
303
		// If the license is disabled, set the transient and disabled flag and return.
304
		if ( isset( $validate->disabled ) ) {
0 ignored issues
show
Bug introduced by
The property disabled does not exist on string|true.
Loading history...
305
			$option['is_disabled'] = true;
306
			$network ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
307
			$this->errors[] = esc_html__( 'Your license key for MonsterInsights has been disabled. Please use a different key.', 'google-analytics-for-wordpress' );
308
309
			return;
310
		}
311
312
		// If forced, set contextual success message.
313
		if ( ( ! empty( $validate->key ) || ! empty( $this->key ) ) && $forced ) {
314
			$key = ! empty( $validate->key ) ? $validate->key : $this->key;
315
			delete_transient( '_monsterinsights_addons' );
316
			monsterinsights_get_addons_data( $key );
317
			$this->success[] = esc_html__( 'Congratulations! Your key has been refreshed successfully.', 'google-analytics-for-wordpress' );
318
		}
319
320
		$option                = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $option is dead and can be removed.
Loading history...
321
		$option                = $network ? MonsterInsights()->license->get_network_license() : MonsterInsights()->license->get_site_license();
322
		$option['type']        = isset( $validate->type ) ? $validate->type : $option['type'];
0 ignored issues
show
Bug introduced by
The property type does not exist on string|true.
Loading history...
323
		$option['is_expired']  = false;
324
		$option['is_disabled'] = false;
325
		$option['is_invalid']  = false;
326
		$network ? MonsterInsights()->license->set_network_license( $option ) : MonsterInsights()->license->set_site_license( $option );
327
328
	}
329
330
	/**
331
	 * Maybe deactivates a license key entered by the user.
332
	 *
333
	 * @since 6.0.0
334
	 *
335
	 * @return null Return early if the key fails to be deactivated.
336
	 */
337
	public function maybe_deactivate_key() {
338
339
		if ( empty( $_POST['monsterinsights-license-key'] ) || empty( $_POST['monsterinsights-deactivate-submit'] ) ) {
340
			return;
341
		}
342
343
		if ( ! wp_verify_nonce( $_POST['monsterinsights-key-nonce'], 'monsterinsights-key-nonce' ) ) {
344
			return;
345
		}
346
347
		if ( ! current_user_can( 'monsterinsights_save_settings' ) ) {
348
			return;
349
		}
350
351
		$this->deactivate_key();
352
353
	}
354
355
	/**
356
	 * Deactivate the license key with ajax.
357
	 */
358
	public function ajax_deactivate_key() {
359
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
360
361
		$network_admin = isset( $_POST['network'] ) && $_POST['network'];
362
363
		$license = ! empty( $_POST['license'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['license'] ) ) ) : false;
364
365
		if ( ! $license ) {
366
			wp_send_json_error(
367
				array(
368
					'error' => esc_html__( 'Please refresh the page and try again.', 'google-analytics-for-wordpress' ),
369
				)
370
			);
371
		}
372
373
		if ( ! current_user_can( 'monsterinsights_save_settings' ) ) {
374
			wp_send_json_error(
375
				array(
376
					'error' => esc_html__( 'You are not allowed to deactivate the license key.', 'google-analytics-for-wordpress' ),
377
				)
378
			);
379
		}
380
381
		$deactivate = $this->perform_remote_request( 'deactivate-key', array( 'tgm-updater-key' => $license ) );
382
383
		if ( ! $deactivate ) {
384
			wp_send_json_error(
385
				array(
386
					'error' => esc_html__( 'There was an error connecting to the remote key API. Please try again later.', 'google-analytics-for-wordpress' ),
387
				)
388
			);
389
		}
390
391
		if ( ! empty( $deactivate->error ) ) {
392
			wp_send_json_error(
393
				array(
394
					'error' => $deactivate->error,
395
				)
396
			);
397
398
		}
399
400
		$network_admin ? MonsterInsights()->license->delete_network_license() : MonsterInsights()->license->delete_site_license();
401
402
		wp_send_json_success(
403
			array(
404
				'message'      => isset( $deactivate->success ) ? $deactivate->success : esc_html__( 'Congratulations! You have deactivated the key from this site successfully.', 'google-analytics-for-wordpress' ),
405
			)
406
		);
407
	}
408
409
	/**
410
	 * Deactivates a license key entered by the user.
411
	 *
412
	 * @since 6.0.0
413
	 */
414
	public function deactivate_key() {
415
416
		// Perform a request to deactivate the key.
417
		$deactivate = $this->perform_remote_request( 'deactivate-key', array( 'tgm-updater-key' => $_POST['monsterinsights-license-key'] ) );
418
419
		// If it returns false, send back a generic error message and return.
420
		if ( ! $deactivate ) {
421
			$this->errors[] = esc_html__( 'There was an error connecting to the remote key API. Please try again later.', 'google-analytics-for-wordpress' );
422
423
			return;
424
		}
425
426
		// If an error is returned, set the error and return.
427
		if ( ! empty( $deactivate->error ) && ! monsterinsights_is_debug_mode() ) {
0 ignored issues
show
Bug introduced by
The property error does not exist on string|true.
Loading history...
428
			$this->errors[] = $deactivate->error;
429
430
			return;
431
		}
432
433
		// Otherwise, our request has been done successfully. Reset the option and set the success message.
434
		$this->success[] = isset( $deactivate->success ) ? $deactivate->success : esc_html__( 'Congratulations! You have deactivated the key from this site successfully.', 'google-analytics-for-wordpress' );
0 ignored issues
show
Bug introduced by
The property success does not exist on string|true.
Loading history...
435
		is_network_admin() ? MonsterInsights()->license->delete_network_license() : MonsterInsights()->license->delete_site_license();
0 ignored issues
show
Bug Best Practice introduced by
The property $license is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
436
437
	}
438
439
	/**
440
	 * Maybe refreshes a license key.
441
	 *
442
	 * @since 6.0.0
443
	 *
444
	 * @return null Return early if the key fails to be refreshed.
445
	 */
446
	public function maybe_refresh_key() {
447
448
		if ( empty( $_POST['monsterinsights-license-key'] ) || empty( $_POST['monsterinsights-refresh-submit'] ) ) {
449
			return;
450
		}
451
452
		if ( ! wp_verify_nonce( $_POST['monsterinsights-key-nonce'], 'monsterinsights-key-nonce' ) ) {
453
			return;
454
		}
455
456
		if ( ! current_user_can( 'monsterinsights_save_settings' ) ) {
457
			return;
458
		}
459
460
		// Refreshing is simply a word alias for validating a key. Force true to set contextual messages.
461
		$this->validate_key( true );
462
463
	}
464
465
	/**
466
	 * Outputs any notices generated by the class.
467
	 *
468
	 * @since 7.0.0
469
	 */
470
	public function monsterinsights_notices() {
471
		if ( ! monsterinsights_is_pro_version() ) {
472
			return;
473
		}
474
475
		$screen = get_current_screen();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $screen is correct as get_current_screen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
476
		if ( empty( $screen->id ) || strpos( $screen->id, 'monsterinsights' ) === false ) {
477
			return;
478
		}
479
480
		if ( ! empty( $this->errors ) ) { ?>
481
			<div class="error">
482
				<p><?php echo implode( '<br>', $this->errors ); ?></p>
483
			</div>
484
		<?php } else if ( ! empty( $this->success ) ) { ?>
485
			<div class="updated">
486
				<p><?php echo implode( '<br>', $this->success ); ?></p>
487
			</div>
488
		<?php }
489
	}
490
491
	/**
492
	 * Queries the remote URL via wp_remote_post and returns a json decoded response.
493
	 *
494
	 * @since 6.0.0
495
	 *
496
	 * @param string $action The name of the $_POST action var.
497
	 * @param array  $body The content to retrieve from the remote URL.
498
	 * @param array  $headers The headers to send to the remote URL.
499
	 * @param string $return_format The format for returning content from the remote URL.
500
	 *
501
	 * @return string|bool          Json decoded response on success, false on failure.
502
	 */
503
	public function perform_remote_request( $action, $body = array(), $headers = array(), $return_format = 'json' ) {
504
505
		// Build the body of the request.
506
		$body = wp_parse_args(
507
			$body,
508
			array(
509
				'tgm-updater-action'     => $action,
510
				'tgm-updater-key'        => $this->key,
511
				'tgm-updater-wp-version' => get_bloginfo( 'version' ),
512
				'tgm-updater-referer'    => site_url(),
513
				'tgm-updater-mi-version' => MONSTERINSIGHTS_VERSION,
514
				'tgm-updater-is-pro'     => monsterinsights_is_pro_version(),
515
			)
516
		);
517
		$body = http_build_query( $body, '', '&' );
518
519
		// Build the headers of the request.
520
		$headers = wp_parse_args(
521
			$headers,
522
			array(
523
				'Content-Type'   => 'application/x-www-form-urlencoded',
524
				'Content-Length' => strlen( $body )
525
			)
526
		);
527
528
		// Setup variable for wp_remote_post.
529
		$post = array(
530
			'headers' => $headers,
531
			'body'    => $body
532
		);
533
534
		// Perform the query and retrieve the response.
535
		$response      = wp_remote_post( monsterinsights_get_licensing_url(), $post );
536
		$response_code = wp_remote_retrieve_response_code( $response );
0 ignored issues
show
Bug introduced by
It seems like $response 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

536
		$response_code = wp_remote_retrieve_response_code( /** @scrutinizer ignore-type */ $response );
Loading history...
537
		$response_body = wp_remote_retrieve_body( $response );
0 ignored issues
show
Bug introduced by
It seems like $response 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

537
		$response_body = wp_remote_retrieve_body( /** @scrutinizer ignore-type */ $response );
Loading history...
538
539
		// Bail out early if there are any errors.
540
		if ( 200 != $response_code || is_wp_error( $response_body ) ) {
541
			return false;
542
		}
543
544
		// Return the json decoded content.
545
		return json_decode( $response_body );
546
	}
547
}
548