Completed
Pull Request — master (#991)
by Zack
12:50 queued 09:33
created

GV_License_Handler::refresh_license_status()   C

Complexity

Conditions 8
Paths 9

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 16
nc 9
nop 0
dl 0
loc 28
ccs 0
cts 20
cp 0
crap 72
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
class GV_License_Handler {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
4
5
	/**
6
	 * @var GravityView_Settings
7
	 */
8
	private $Addon;
9
10
	const name = 'GravityView';
11
12
	const author = 'Katz Web Services, Inc.';
13
	
14
	const url = 'https://gravityview.co';
15
	
16
	const version = GravityView_Plugin::version;
17
18
	/**
19
	 * Post ID on gravityview.co
20
	 * @since 1.15
21
	 */
22
	const item_id = 17;
23
24
	/**
25
	 * Name of the transient used to store license status for GV
26
	 * @since 1.17
27
	 */
28
	const status_transient_key = 'gravityview_edd-activate_valid';
29
30
	/**
31
	 * @var string Key used to store active GravityView/Gravity Forms plugin data
32
	 * @since 1.15
33
	 */
34
	const related_plugins_key = 'gravityview_related_plugins';
35
36
	/** @var EDD_SL_Plugin_Updater */
37
	private $EDD_SL_Plugin_Updater;
38
39
	/**
40
	 * @var GV_License_Handler
41
	 */
42
	public static $instance;
43
44
	/**
45
	 * @param GravityView_Settings $GFAddOn
46
	 *
47
	 * @return GV_License_Handler
48
	 */
49
	public static function get_instance( GravityView_Settings $GFAddOn ) {
50
		if( empty( self::$instance ) ) {
51
			self::$instance = new self( $GFAddOn );
52
		}
53
		return self::$instance;
54
	}
55
	
56
	private function __construct( GravityView_Settings $GFAddOn ) {
57
58
		$this->Addon = $GFAddOn;
59
60
		$this->add_hooks();
61
	}
62
63
	private function add_hooks() {
64
		add_action( 'admin_init', array( $this, 'setup_edd' ), 0 );
65
		add_action( 'wp_ajax_gravityview_license', array( $this, 'license_call' ) );
66
		add_action( 'admin_init', array( $this, 'refresh_license_status' ) );
67
		add_action( 'admin_init', array( $this, 'check_license' ) );
68
		add_action( 'update_option_active_plugins', array( $this, 'flush_related_plugins_transient' ) );
69
		add_action( 'update_option_active_sitewide_plugins', array( $this, 'flush_related_plugins_transient' ) );
70
	}
71
72
	/**
73
	 * When a plugin is activated or deactivated, delete the cached extensions/plugins used by get_related_plugins_and_extensions()
74
	 *
75
	 * @see get_related_plugins_and_extensions()
76
	 * @since 1.15
77
	 */
78
	public function flush_related_plugins_transient() {
79
		if ( function_exists( 'delete_site_transient' ) ) {
80
			delete_site_transient( self::related_plugins_key );
81
		}
82
	}
83
84
	/**
85
	 * Check the GravityView license information
86
	 *
87
	 * @since 1.19.3
88
	 *
89
	 * @param bool $force Whether to force checking license, even if AJAX
0 ignored issues
show
Bug introduced by
There is no parameter named $force. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
90
	 *
91
	 * @return void
92
	 */
93
	public function check_license() {
94
95
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
96
			return; // Don't fire when saving settings or AJAX
97
		}
98
99
		if ( ! apply_filters( 'gv_send_site_data', true ) ) {
100
			return;
101
		}
102
103
		// Send checkins once per week
104
		$last_checked = get_option( 'gv_last_checkin', false );
105
106
		if ( is_numeric( $last_checked ) && $last_checked > strtotime( '-1 week', current_time( 'timestamp' ) ) ) {
107
			return; // checked within a week
108
		}
109
110
		$status = get_transient( 'gv_license_check' );
111
112
		// Run the license check a maximum of once per day, and not on GV website
113
		if ( false === $status && site_url() !== self::url ) {
114
115
			// Call the custom API.
116
			$response = wp_remote_post( self::url, array(
117
				'timeout'   => 15,
118
			    'sslverify' => false,
119
			    'body'      =>  array(
0 ignored issues
show
introduced by
Expected 1 space after "=>"; 2 found
Loading history...
120
				    'edd_action' => 'check_license',
121
				    'license'    => trim( $this->Addon->get_app_setting( 'license_key' ) ),
122
				    'item_name'  => self::name,
123
				    'url'        => home_url(),
124
				    'site_data'  => $this->get_site_data(),
125
			    ),
126
			));
127
128
			// make sure the response came back okay
129
			if ( is_wp_error( $response ) ) {
130
131
				// Connection failed, try again in three hours
132
				set_transient( 'gv_license_check', 1, 3 * HOUR_IN_SECONDS );
133
134
				return;
135
			}
136
137
			set_transient( 'gv_license_check', 1, DAY_IN_SECONDS );
138
139
			update_option( 'gv_last_checkin', current_time( 'timestamp' ) );
140
		}
141
	}
142
143
	/**
144
	 * When the status transient expires (or is deleted on activation), re-check the status
145
	 *
146
	 * @since 1.17
147
	 *
148
	 * @return void
149
	 */
150
	public function refresh_license_status() {
151
152
		if ( defined('DOING_AJAX') && DOING_AJAX ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
153
			return;
154
		}
155
156
		global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
157
158
		$is_force_check = ( isset( $pagenow ) && 'update-core.php' === $pagenow && isset( $_REQUEST['force-check'] ) && '1' === $_REQUEST['force-check'] );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
159
160
		// The transient is fresh; don't fetch.
161
		if( $status = get_transient( self::status_transient_key ) && ! $is_force_check ) {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $status = (get_transient...y) && !$is_force_check), Probably Intended Meaning: ($status = get_transient...y)) && !$is_force_check
Loading history...
162
			return;
163
		}
164
165
		$data = array(
166
			'edd_action' => 'check_license',
167
			'license' => trim( $this->Addon->get_app_setting( 'license_key' ) ),
168
			'update' => true,
169
			'format' => 'object',
170
			'all_caps' => true,
171
			'field_id' => 'refresh_license_status', // Required to set the `status_transient_key` transient
172
		);
173
174
		$license_call = GravityView_Settings::get_instance()->get_license_handler()->license_call( $data );
175
176
		do_action( 'gravityview_log_debug', __METHOD__ . ': Refreshed the license.', $license_call );
177
	}
178
179
	/**
180
	 * Retrieves site data (plugin versions, integrations, etc) to be sent along with the license check.
181
	 *
182
	 * @since 1.9
183
	 * @access public
184
	 *
185
	 * @return array
186
	 */
187
	public function get_site_data() {
188
189
		$data = array();
190
191
		$theme_data = wp_get_theme();
192
		$theme      = $theme_data->Name . ' ' . $theme_data->Version;
193
194
		$data['gv_version']  = GravityView_Plugin::version;
195
		$data['php_version']  = phpversion();
196
		$data['wp_version']   = get_bloginfo( 'version' );
197
		$data['gf_version']  = GFForms::$version;
198
		$data['server']       = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
0 ignored issues
show
introduced by
Detected usage of a non-sanitized input variable: $_SERVER
Loading history...
199
		$data['multisite']    = is_multisite();
200
		$data['theme']        = $theme;
201
		$data['url']          = home_url();
202
		$data['license_key']  = GravityView_Settings::get_instance()->get_app_setting( 'license_key' );
203
		$data['beta']         = GravityView_Settings::get_instance()->get_app_setting( 'beta' );
204
205
		// View Data
206
		$gravityview_posts = get_posts('numberposts=-1&post_type=gravityview&post_status=publish&order=ASC');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
introduced by
Disabling pagination is prohibited in VIP context, do not set numberposts to -1 ever.
Loading history...
207
208
		if ( ! empty( $gravityview_posts ) ) {
209
			$first = array_shift( $gravityview_posts );
210
			$latest = array_pop( $gravityview_posts );
211
			$data['view_count'] = count( $gravityview_posts );
212
			$data['view_first'] = $first->post_date;
213
			$data['view_latest'] = $latest->post_date;
214
		}
215
216
		// Form counts
217
		if ( class_exists( 'GFFormsModel' ) ) {
218
			$form_data = GFFormsModel::get_form_count();
219
			$data['forms_total'] = rgar( $form_data, 'total', 0 );
220
			$data['forms_active'] = rgar( $form_data, 'active', 0 );
221
			$data['forms_inactive'] = rgar( $form_data, 'inactive', 0 );
222
			$data['forms_trash'] = rgar( $form_data, 'inactive', 0 );
223
		}
224
225
		// Retrieve current plugin information
226
		if( ! function_exists( 'get_plugins' ) ) {
227
			include ABSPATH . '/wp-admin/includes/plugin.php';
228
		}
229
230
		$data['integrations']     = self::get_related_plugins_and_extensions();
231
		$data['active_plugins']   = get_option( 'active_plugins', array() );
232
		$data['inactive_plugins'] = array();
233
		$data['locale']           = get_locale();
234
235
		// Validate request on the GV server
236
		$data['hash']             = 'gv_version.url.locale:' . sha1( $data['gv_version'] . $data['url'] . $data['locale'] );
237
238
		return $data;
239
	}
240
241
	/**
242
	 * Get active GravityView Extensions and Gravity Forms Add-ons to help debug issues.
243
	 *
244
	 * @since 1.15
245
	 * @return string List of active extensions related to GravityView or Gravity Forms, separated by HTML line breaks
246
	 */
247
	static public function get_related_plugins_and_extensions( $implode = '<br />' ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
248
249
		if ( ! function_exists( 'wp_get_active_and_valid_plugins' ) ) {
250
			return 'Running < WP 3.0';
251
		}
252
253
		$extensions = get_site_transient( self::related_plugins_key );
254
255
		if ( empty( $extensions ) ) {
256
257
			$active_plugins = wp_get_active_and_valid_plugins();
258
			$extensions = array();
259
			foreach ( $active_plugins as $active_plugin ) {
260
261
				// Match gravityview, gravity-forms, gravityforms, gravitate
262
				if ( ! preg_match( '/(gravityview|gravity-?forms|gravitate)/ism', $active_plugin ) ) {
263
					continue;
264
				}
265
266
				$plugin_data = get_plugin_data( $active_plugin );
267
268
				$extensions[] = sprintf( '%s %s', $plugin_data['Name'], $plugin_data['Version'] );
269
			}
270
271
			if( ! empty( $extensions ) ) {
272
				set_site_transient( self::related_plugins_key, $extensions, HOUR_IN_SECONDS );
273
			} else {
274
				return 'There was an error fetching related plugins.';
275
			}
276
		}
277
278
		return $implode ? implode( $implode, $extensions ) : $extensions;
279
	}
280
281
	function settings_edd_license_activation( $field, $echo ) {
0 ignored issues
show
Unused Code introduced by
The parameter $field 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...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
282
283
		$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
284
285
		wp_enqueue_script( 'gv-admin-edd-license', GRAVITYVIEW_URL . 'assets/js/admin-edd-license' . $script_debug . '.js', array( 'jquery' ) );
286
287
		$status = trim( $this->Addon->get_app_setting( 'license_key_status' ) );
288
		$key = trim( $this->Addon->get_app_setting( 'license_key' ) );
289
290
		if( !empty( $key ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
291
			$response = $this->Addon->get_app_setting( 'license_key_response' );
292
			$response = is_array( $response ) ? (object) $response : json_decode( $response );
293
		} else {
294
			$response = array();
295
		}
296
297
		wp_localize_script( 'gv-admin-edd-license', 'GVGlobals', array(
298
			'license_box' => $this->get_license_message( $response )
299
		));
300
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
301
302
		$fields = array(
303
			array(
304
				'name'  => 'edd-activate',
305
				'value' => __('Activate License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
306
				'data-pending_text' => __('Verifying license&hellip;', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
307
				'data-edd_action' => 'activate_license',
308
				'class' => 'button-primary',
309
			),
310
			array(
311
				'name'  => 'edd-deactivate',
312
				'value' => __('Deactivate License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
313
				'data-pending_text' => __('Deactivating license&hellip;', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
314
				'data-edd_action' => 'deactivate_license',
315
				'class' => ( empty( $status ) ? 'button-primary hide' : 'button-primary' ),
316
			),
317
			array(
318
				'name'  => 'edd-check',
319
				'value' => __('Check License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
320
				'data-pending_text' => __('Verifying license&hellip;', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
321
				'title' => 'Check the license before saving it',
322
				'data-edd_action' => 'check_license',
323
				'class' => 'button-secondary',
324
			),
325
		);
326
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
327
328
		$class = 'button gv-edd-action';
329
330
		$class .= ( !empty( $key ) && $status !== 'valid' ) ? '' : ' hide';
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
331
332
		$disabled_attribute = GVCommon::has_cap( 'gravityview_edit_settings' ) ? false : 'disabled';
333
334
		$submit = '<div class="gv-edd-button-wrapper">';
335
		foreach ( $fields as $field ) {
336
			$field['type'] = 'button';
337
			$field['class'] = isset( $field['class'] ) ? $field['class'] . ' '. $class : $class;
338
			$field['style'] = 'margin-left: 10px;';
339
			if( $disabled_attribute ) {
340
				$field['disabled'] = $disabled_attribute;
341
			}
342
			$submit .= $this->Addon->settings_submit( $field, $echo );
343
		}
344
		$submit .= '</div>';
345
346
		return $submit;
347
	}
348
349
	/**
350
	 * Include the EDD plugin updater class, if not exists
351
	 *
352
	 * @since 1.7.4
353
	 * @since 1.21.5.3 Changed visibility of method to public
354
	 *
355
	 * @return void
356
	 */
357
	public function setup_edd() {
358
359
		if( !class_exists('EDD_SL_Plugin_Updater') ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
360
			require_once( GRAVITYVIEW_DIR . 'includes/lib/EDD_SL_Plugin_Updater.php');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
361
		}
362
363
		// setup the updater
364
		$this->EDD_SL_Plugin_Updater = new EDD_SL_Plugin_Updater(
365
			self::url,
366
			GRAVITYVIEW_FILE,
367
			$this->_get_edd_settings()
368
		);
369
370
	}
371
372
	/**
373
	 * Generate the array of settings passed to the EDD license call
374
	 *
375
	 * @since 1.7.4
376
	 *
377
	 * @param string $action The action to send to edd, such as `check_license`
378
	 * @param string $license The license key to have passed to EDD
379
	 *
380
	 * @return array
381
	 */
382
	function _get_edd_settings( $action = '', $license = '' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
383
384
		// retrieve our license key from the DB
385
		$license_key = empty( $license ) ? trim( $this->Addon->get_app_setting( 'license_key' ) ) : $license;
386
387
		$settings = array(
388
			'version'   => self::version,
389
			'license'   => $license_key,
390
			'item_name' => self::name,
391
			'item_id'   => self::item_id,
392
			'author'    => self::author,
393
			'language'  => get_locale(),
394
			'url'       => home_url(),
395
		    'beta'      => $this->Addon->get_app_setting( 'beta' ),
396
		);
397
398
		if( !empty( $action ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
399
			$settings['edd_action'] = esc_attr( $action );
400
		}
401
402
		$settings = array_map( 'urlencode', $settings );
403
404
		return $settings;
405
	}
406
407
	/**
408
	 * Perform the call
409
	 * @return array|WP_Error
410
	 */
411
	private function _license_get_remote_response( $data, $license = '' ) {
412
413
		$api_params = $this->_get_edd_settings( $data['edd_action'], $license );
414
415
		$url = add_query_arg( $api_params, self::url );
416
417
		$response = wp_remote_get( $url, array(
0 ignored issues
show
introduced by
wp_remote_get is highly discouraged, please use vip_safe_wp_remote_get() instead.
Loading history...
418
			'timeout'   => 15,
419
			'sslverify' => false,
420
		));
421
422
		if ( is_wp_error( $response ) ) {
423
424
			do_action( 'gravityview_log_error', 'WP_Error response from license check. API params:', $api_params );
425
426
			return array();
427
		}
428
429
		$license_data = json_decode( wp_remote_retrieve_body( $response ) );
430
431
		// Not JSON
432
		if ( empty( $license_data ) ) {
433
434
			do_action( 'gravityview_log_error', 'Empty license data response from license check', compact( 'response', 'url', 'api_params', 'data' ) );
435
436
			delete_transient( self::status_transient_key );
437
438
			// Change status
439
			return array();
440
		}
441
442
		// Store the license key inside the data array
443
		$license_data->license_key = $license;
444
445
		return $license_data;
446
	}
447
448
	/**
449
	 * Generate the status message displayed in the license field
450
	 *
451
	 * @since 1.7.4
452
	 * @param $license_data
453
	 *
454
	 * @return string
455
	 */
456
	function get_license_message( $license_data ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
457
458
		if( empty( $license_data ) ) {
459
			$message = '';
460
		} else {
461
462
			if( ! empty( $license_data->error ) ) {
463
				$class = 'error';
464
				$string_key = $license_data->error;
465
			} else {
466
				$class = $license_data->license;
467
				$string_key = $license_data->license;
468
			}
469
470
			$message = sprintf( '<p><strong>%s: %s</strong></p>', $this->strings('status'), $this->strings( $string_key, $license_data ) );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
471
472
			$message = $this->generate_license_box( $message, $class );
473
		}
474
475
		return $message;
476
	}
477
478
	/**
479
	 * Generate the status message box HTML based on the current status
480
	 *
481
	 * @since 1.7.4
482
	 * @param $message
483
	 * @param string $class
484
	 *
485
	 * @return string
486
	 */
487
	private function generate_license_box( $message, $class = '' ) {
488
489
		$template = '<div id="gv-edd-status" aria-live="polite" aria-busy="false" class="gv-edd-message inline %s">%s</div>';
490
491
		$output = sprintf( $template, esc_attr( $class ), $message );
492
493
		return $output;
494
	}
495
496
	/**
497
	 * Allow pure HTML in settings fields
498
	 *
499
	 * @since 1.17
500
	 *
501
	 * @param array $response License response
502
	 *
503
	 * @return string `html` key of the $field
504
	 */
505
	public function license_details( $response = array() ) {
506
507
		$response = (array) $response;
508
509
		$return = '';
510
		$wrapper = '<span class="gv-license-details" aria-live="polite" aria-busy="false">%s</span>';
511
512
		if( ! empty( $response['license_key'] ) ) {
513
514
			$return .= '<h3>' . esc_html__( 'License Details:', 'gravityview' ) . '</h3>';
515
516
			if ( in_array( rgar( $response, 'license' ), array( 'invalid', 'deactivated' ) ) ) {
517
				$return .= $this->strings( $response['license'], $response );
518
			} elseif ( ! empty( $response['license_name'] ) ) {
519
520
				$response_keys = array(
521
					'license_name'   => '',
522
					'license_limit'  => '',
523
					'customer_name'  => '',
524
					'customer_email' => '',
525
					'site_count'     => '',
526
					'expires'        => '',
527
					'upgrades'       => ''
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
528
				);
529
530
				// Make sure all the keys are set
531
				$response = wp_parse_args( $response, $response_keys );
532
533
				$login_link = sprintf( '<a href="%s" class="howto" rel="external">%s</a>', esc_url( sprintf( 'https://gravityview.co/wp-login.php?username=%s', $response['customer_email'] ) ), esc_html__( 'Access your GravityView account', 'gravityview' ) );
534
				$local_text = ( ! empty( $response['is_local'] ) ? '<span class="howto">' . __( 'This development site does not count toward license activation limits', 'gravityview' ) . '</span>' : '' );
535
				$details    = array(
536
					'license'     => sprintf( esc_html__( 'License level: %s', 'gravityview' ), esc_html( $response['license_name'] ), esc_html( $response['license_limit'] ) ),
537
					'licensed_to' => sprintf( esc_html_x( 'Licensed to: %1$s (%2$s)', '1: Customer name; 2: Customer email', 'gravityview' ), esc_html__( $response['customer_name'], 'gravityview' ), esc_html__( $response['customer_email'], 'gravityview' ) ) . $login_link,
538
					'activations' => sprintf( esc_html__( 'Activations: %d of %s sites', 'gravityview' ), intval( $response['site_count'] ), esc_html( $response['license_limit'] ) ) . $local_text,
539
					'expires'     => 'lifetime' === $response['expires'] ? '' : sprintf( esc_html__( 'Renew on: %s', 'gravityview' ), date_i18n( get_option( 'date_format' ), strtotime( $response['expires'] ) - DAY_IN_SECONDS ) ),
540
					'upgrade'     => $this->get_upgrade_html( $response['upgrades'] ),
541
				);
542
543
				if ( ! empty( $response['error'] ) && 'expired' === $response['error'] ) {
544
					unset( $details['upgrade'] );
545
					$details['expires'] = '<div class="error inline"><p>' . $this->strings( 'expired', $response ) . '</p></div>';
546
				}
547
548
				$return .= '<ul><li>' . implode( '</li><li>', array_filter( $details ) ) . '</li></ul>';
549
			}
550
		}
551
552
		return sprintf( $wrapper, $return );
553
	}
554
555
	/**
556
	 * Display possible upgrades for a license
557
	 *
558
	 * @since 1.17
559
	 *
560
	 * @param array $upgrades Array of upgrade paths, returned from the GV website
561
	 *
562
	 * @return string HTML list of upgrades available for the current license
563
	 */
564
	function get_upgrade_html( $upgrades ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
565
566
		$output = '';
567
568
		if( ! empty( $upgrades ) ) {
569
570
			$locale_parts = explode( '_', get_locale() );
571
572
			$is_english = ( 'en' === $locale_parts[0] );
573
574
			$output .= '<h4>' . esc_html__( 'Upgrades available:', 'gravityview' ) . '</h4>';
575
576
			$output .= '<ul class="ul-disc">';
577
578
			foreach ( $upgrades as $upgrade_id => $upgrade ) {
579
580
				$upgrade = (object) $upgrade;
581
582
				$anchor_text = sprintf( esc_html_x( 'Upgrade to %1$s for %2$s', '1: GravityView upgrade name, 2: Cost of upgrade', 'gravityview' ), esc_attr( $upgrade->name ), esc_attr( $upgrade->price ) );
583
584
				if( $is_english && isset( $upgrade->description ) ) {
585
					$message = esc_html( $upgrade->description );
586
				} else {
587
					switch( $upgrade->price_id ) {
588
						// Interstellar
589
						case 1:
590
						default:
591
							$message = esc_html__( 'Get access to Extensions', 'gravityview' );
592
							break;
593
						// Galactic
594
						case 2:
0 ignored issues
show
Unused Code introduced by
case 2: $message = e...avityview'); break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
595
							$message = esc_html__( 'Get access to Entry Importer and other Premium plugins', 'gravityview' );
596
							break;
597
					}
598
				}
599
600
				$output .= sprintf( '<li><a href="%s">%s</a><span class="howto">%s</span></li>', esc_url( add_query_arg( array( 'utm_source' => 'settings', 'utm_medium' => 'admin', 'utm_content' => 'license-details', 'utm_campaign' => 'Upgrades' ), $upgrade->url ) ), $anchor_text, $message );
601
			}
602
			$output .= '</ul>';
603
		}
604
605
		return $output;
606
	}
607
608
	/**
609
	 * Perform the call to EDD based on the AJAX call or passed data
610
	 *
611
	 * @since 1.7.4
612
	 *
613
	 * @param array $array {
614
	 * @type string $license The license key
615
	 * @type string $edd_action The EDD action to perform, like `check_license`
616
	 * @type string $field_id The ID of the field to check
617
	 * @type boolean $update Whether to update plugin settings. Prevent updating the data by setting an `update` key to false
618
	 * @type string $format If `object`, return the object of the license data. Else, return the JSON-encoded object
619
	 * }
620
	 *
621
	 * @return mixed|string|void
622
	 */
623
	public function license_call( $array = array() ) {
624
625
		$is_ajax = ( defined('DOING_AJAX') && DOING_AJAX );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
626
		$data = empty( $array ) ? $_POST['data'] : $array;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
627
		$has_cap = GVCommon::has_cap( 'gravityview_edit_settings' );
628
629
		if ( $is_ajax && empty( $data['license'] ) ) {
630
			die( - 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method license_call() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
631
		}
632
633
		// If the user isn't allowed to edit settings, show an error message
634
		if( ! $has_cap && empty( $data['all_caps'] ) ) {
635
			$license_data = new stdClass();
636
			$license_data->error = 'capability';
637
			$license_data->message = $this->get_license_message( $license_data );
638
			$json = json_encode( $license_data );
639
		} else {
640
641
			$license      = esc_attr( rgget( 'license', $data ) );
642
			$license_data = $this->_license_get_remote_response( $data, $license );
643
644
			// Empty is returned when there's an error.
645
			if ( empty( $license_data ) ) {
646
				if ( $is_ajax ) {
647
					exit( json_encode( array() ) );
0 ignored issues
show
Coding Style Compatibility introduced by
The method license_call() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
648
				} else { // Non-ajax call
649
					return json_encode( array() );
650
				}
651
			}
652
653
			$license_data->details = $this->license_details( $license_data );
654
			$license_data->message = $this->get_license_message( $license_data );
655
656
			$json = json_encode( $license_data );
657
658
			$update_license = ( ! isset( $data['update'] ) || ! empty( $data['update'] ) );
659
660
			$is_check_action_button = ( 'check_license' === $data['edd_action'] && defined( 'DOING_AJAX' ) && DOING_AJAX );
661
662
			if( $is_check_action_button ) {
663
				delete_transient( self::status_transient_key );
664
			}
665
			// Failed is the response from trying to de-activate a license and it didn't work.
666
			// This likely happened because people entered in a different key and clicked "Deactivate",
667
			// meaning to deactivate the original key. We don't want to save this response, since it is
668
			// most likely a mistake.
669
			else if ( $license_data->license !== 'failed' && $update_license ) {
0 ignored issues
show
introduced by
Found "!== '". Use Yoda Condition checks, you must
Loading history...
670
671
				if ( ! empty( $data['field_id'] ) ) {
672
					set_transient( self::status_transient_key, $license_data, DAY_IN_SECONDS );
673
				}
674
675
				$this->license_call_update_settings( $license_data, $data );
676
			}
677
		} // End $has_cap
678
679
		if ( $is_ajax ) {
680
			exit( $json );
0 ignored issues
show
Coding Style Compatibility introduced by
The method license_call() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
681
		} else { // Non-ajax call
682
			return ( rgget('format', $data ) === 'object' ) ? $license_data : $json;
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
683
		}
684
	}
685
686
	/**
687
	 * Update the license after fetching it
688
	 * @param object $license_data
689
	 * @return void
690
	 */
691
	private function license_call_update_settings( $license_data, $data ) {
692
693
		// Update option with passed data license
694
		$settings = $this->Addon->get_app_settings();
695
696
        $settings['license_key'] = $license_data->license_key = trim( $data['license'] );
697
		$settings['license_key_status'] = $license_data->license;
698
		$settings['license_key_response'] = (array)$license_data;
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
699
700
		$this->Addon->update_app_settings( $settings );
701
	}
702
703
	/**
704
	 * URL to direct license renewal, or if license key is not set, then just the account page
705
	 * @since 1.13.1
706
	 * @param  object|null $license_data Object with license data
707
	 * @return string Renewal or account URL
708
	 */
709
	private function get_license_renewal_url( $license_data ) {
710
711
		$license_data = is_array( $license_data ) ? (object)$license_data : $license_data;
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
712
713
		if( ! empty( $license_data->renewal_url ) ) {
714
			$renew_license_url = $license_data->renewal_url;
715
		} elseif( ! empty( $license_data->license_key ) ) {
716
			$renew_license_url = sprintf( 'https://gravityview.co/checkout/?download_id=17&edd_license_key=%s', $license_data->license_key );
717
		} else {
718
			$renew_license_url = 'https://gravityview.co/account/';
719
		}
720
721
		$renew_license_url = add_query_arg( wp_parse_args( 'utm_source=admin_notice&utm_medium=admin&utm_content=expired&utm_campaign=Activation&force_login=1' ), $renew_license_url );
722
723
		return $renew_license_url;
724
	}
725
726
	/**
727
	 * Override the text used in the GravityView EDD license Javascript
728
	 *
729
	 * @param  array|null $status Status to get. If empty, get all strings.
730
	 * @param  object|null $license_data Object with license data
731
	 * @return array          Modified array of content
732
	 */
733
	public function strings( $status = NULL, $license_data = null ) {
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
734
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
735
736
		$strings = array(
737
			'status' => esc_html__('Status', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
738
			'error' => esc_html__('There was an error processing the request.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
739
			'failed'  => esc_html__('Could not deactivate the license. The license key you attempted to deactivate may not be active or valid.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
740
			'site_inactive' => esc_html__('The license key is valid, but it has not been activated for this site.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
741
			'inactive' => esc_html__('The license key is valid, but it has not been activated for this site.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
742
			'no_activations_left' => esc_html__('Invalid: this license has reached its activation limit.', 'gravityview') . ' ' . sprintf( esc_html__('You can manage license activations %son your GravityView account page%s.', 'gravityview'), '<a href="https://gravityview.co/account/#licenses">', '</a>' ),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
743
			'deactivated' => esc_html__('The license has been deactivated.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
744
			'valid' => esc_html__('The license key is valid and active.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
745
			'invalid' => esc_html__('The license key entered is invalid.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
746
			'missing' => esc_html__('Invalid license key.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
747
			'revoked' => esc_html__('This license key has been revoked.', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
748
			'expired' => sprintf( esc_html__('This license key has expired. %sRenew your license on the GravityView website%s to receive updates and support.', 'gravityview'), '<a href="'. esc_url( $this->get_license_renewal_url( $license_data ) ) .'">', '</a>' ),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
749
			'capability' => esc_html__( 'You don\'t have the ability to edit plugin settings.', 'gravityview' ),
750
751
			'verifying_license' => esc_html__('Verifying license&hellip;', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
752
			'activate_license' => esc_html__('Activate License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
753
			'deactivate_license' => esc_html__('Deactivate License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
754
			'check_license' => esc_html__('Verify License', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
755
		);
756
757
		if( empty( $status ) ) {
758
			return $strings;
759
		}
760
761
		if( isset( $strings[ $status ] ) ) {
762
			return $strings[ $status ];
763
		}
764
765
		return NULL;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
766
	}
767
768
}