PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.9
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.9
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / vendor / codeinwp / themeisle-sdk / src / Modules / Licenser.php

Licenser.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.7.9, at vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php

1,054 lines 31.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The main loader class for license handling.
4 *
5 * @package ThemeIsleSDK
6 * @subpackage Modules
7 * @copyright Copyright (c) 2017, Marius Cristea
8 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
9 * @since 1.0.0
10 */
11
12 namespace ThemeisleSDK\Modules;
13
14 // Exit if accessed directly.
15 use ThemeisleSDK\Common\Abstract_Module;
16 use ThemeisleSDK\Loader;
17 use ThemeisleSDK\Product;
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 /**
24 * Licenser module for ThemeIsle SDK.
25 */
26 class Licenser extends Abstract_Module {
27
28 /**
29 * Number of max failed checks before showing the license message.
30 *
31 * @var int $max_failed Maximum failed checks allowed before show the notice
32 */
33 private static $max_failed = 2;
34 /**
35 * License key string.
36 *
37 * @var string $license_key The license key string
38 */
39 public $license_key;
40 /**
41 * This ensures that the custom API request only runs on the second time that WP fires the update check.
42 *
43 * @var bool $do_check Flag for request.
44 */
45 private $do_check = false;
46 /**
47 * Number of failed checks to the api endpoint.
48 *
49 * @var bool $failed_checks
50 */
51 private $failed_checks = 0;
52 /**
53 * The product update response key.
54 *
55 * @var string $product_key Product key.
56 */
57 private $product_key;
58
59 /**
60 * Holds local license object.
61 *
62 * @var null Local license object.
63 */
64 private $license_local = null;
65 /**
66 * Product namespace, used for fixed name filters/cli commands.
67 *
68 * @var string $namespace Product namespace.
69 */
70 private $namespace = null;
71
72 /**
73 * Disable wporg updates for premium products.
74 *
75 * @param string $r Update payload.
76 * @param string $url The api url.
77 *
78 * @return mixed List of themes to check for update.
79 */
80 public function disable_wporg_update( $r, $url ) {
81
82 if ( 0 !== strpos( $url, 'https://api.wordpress.org/themes/update-check/' ) ) {
83 return $r;
84 }
85
86 // Decode the JSON response.
87 $themes = json_decode( $r['body']['themes'] );
88
89 unset( $themes->themes->{$this->product->get_slug()} );
90
91 // Encode the updated JSON response.
92 $r['body']['themes'] = wp_json_encode( $themes );
93
94 return $r;
95 }
96
97 /**
98 * Register the setting for the license of the product.
99 *
100 * @return bool
101 */
102 public function register_settings() {
103 if ( ! is_admin() ) {
104 return false;
105 }
106 if ( apply_filters( $this->product->get_key() . '_hide_license_field', false ) ) {
107 return;
108 }
109 add_settings_field(
110 $this->product->get_key() . '_license',
111 $this->product->get_name() . ' license',
112 array( $this, 'license_view' ),
113 'general'
114 );
115 }
116
117 /**
118 * The license view field.
119 */
120 public function license_view() {
121 $status = $this->get_license_status();
122 $value = $this->license_key;
123
124 $activate_string = apply_filters( $this->product->get_key() . '_lc_activate_string', 'Activate' );
125 $deactivate_string = apply_filters( $this->product->get_key() . '_lc_deactivate_string', 'Deactivate' );
126 $valid_string = apply_filters( $this->product->get_key() . '_lc_valid_string', 'Valid' );
127 $invalid_string = apply_filters( $this->product->get_key() . '_lc_invalid_string', 'Invalid' );
128 $license_message = apply_filters( $this->product->get_key() . '_lc_license_message', 'Enter your license from %s purchase history in order to get %s updates' );
129 $error_message = $this->get_error();
130 ?>
131 <style type="text/css">
132 input.themeisle-sdk-text-input-valid {
133 border: 1px solid #7ad03a;
134 }
135
136 input.themeisle-sdk-license-input {
137 width: 300px;
138 padding: 0 8px;
139 line-height: 2;
140 min-height: 30px;
141 }
142
143 .themeisle-sdk-license-deactivate-cta {
144 color: #fff;
145 background: #7ad03a;
146 display: inline-block;
147 text-decoration: none;
148 font-size: 13px;
149 line-height: 30px;
150 height: 26px;
151 margin-left: 5px;
152 padding: 0 10px 3px;
153 -webkit-border-radius: 3px;
154 border-radius: 3px;
155 }
156
157 .themeisle-sdk-license-activate-cta {
158 color: #fff;
159 background: #dd3d36;
160 display: inline-block;
161 text-decoration: none;
162 font-size: 13px;
163 line-height: 30px;
164 height: 26px;
165 margin-left: 5px;
166 padding: 0 10px 3px;
167 -webkit-border-radius: 3px;
168 border-radius: 3px;
169 }
170
171 button.button.themeisle-sdk-licenser-button-cta {
172 line-height: 26px;
173 height: 29px;
174 vertical-align: top;
175 }
176
177 </style>
178 <?php
179 echo sprintf(
180 '<p>%s<input class="themeisle-sdk-license-input %s" type="text" id="%s_license" name="%s_license" value="%s" /><a class="%s">%s</a>&nbsp;&nbsp;&nbsp;<button name="%s_btn_trigger" class="button button-primary themeisle-sdk-licenser-button-cta" value="yes" type="submit" >%s</button></p><p class="description">%s</p>%s',
181 ( ( 'valid' === $status ) ? sprintf( '<input type="hidden" value="%s" name="%s_license" />', esc_attr( $value ), esc_attr( $this->product->get_key() ) ) : '' ),
182 ( ( 'valid' === $status ) ? 'themeisle-sdk-text-input-valid' : '' ),
183 esc_attr( $this->product->get_key() ),
184 esc_attr( ( ( 'valid' === $status ) ? $this->product->get_key() . '_mask' : $this->product->get_key() ) ),
185 esc_attr( ( ( 'valid' === $status ) ? ( str_repeat( '*', 30 ) . substr( $value, - 5 ) ) : $value ) ),
186 esc_attr( ( 'valid' === $status ? 'themeisle-sdk-license-deactivate-cta' : 'themeisle-sdk-license-activate-cta' ) ),
187 esc_attr( 'valid' === $status ? $valid_string : $invalid_string ),
188 esc_attr( $this->product->get_key() ),
189 esc_attr( 'valid' === $status ? $deactivate_string : $activate_string ),
190 sprintf( wp_kses_data( $license_message ), '<a href="' . esc_url( $this->get_api_url() ) . '">' . esc_attr( $this->get_distributor_name() ) . '</a> ', esc_attr( $this->product->get_type() ) ),
191 wp_kses_data( empty( $error_message ) ? '' : sprintf( '<p style="color:#dd3d36">%s</p>', ( $error_message ) ) )
192 ) . wp_nonce_field( $this->product->get_key() . 'nonce', $this->product->get_key() . 'nonce_field', false, false );//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
193
194 }
195
196 /**
197 * Return the license status.
198 *
199 * @param bool $check_expiration Should check if license is valid, but expired.
200 *
201 * @return string The License status.
202 */
203 public function get_license_status( $check_expiration = false ) {
204
205 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
206
207 if ( '' === $license_data ) {
208 return get_option( $this->product->get_key() . '_license_status', 'not_active' );
209 }
210 $status = isset( $license_data->license ) ? $license_data->license : get_option( $this->product->get_key() . '_license_status', 'not_active' );
211 if ( false === $check_expiration ) {
212 return $status;
213 }
214
215 return ( 'valid' === $status && isset( $license_data->is_expired ) && 'yes' === $license_data->is_expired ) ? 'active_expired' : $status;
216 }
217
218 /**
219 * Return the last error message.
220 *
221 * @return mixed Error message.
222 */
223 public function get_error() {
224 return get_transient( $this->product->get_key() . 'act_err' );
225 }
226
227 /**
228 * Get remote api url.
229 *
230 * @return string Remote api url.
231 */
232 public function get_api_url() {
233 if ( $this->is_from_partner( $this->product ) ) {
234 return 'https://themeisle.com';
235 }
236
237 return $this->product->get_store_url();
238 }
239
240 /**
241 * Get remote api url.
242 *
243 * @return string Remote api url.
244 */
245 public function get_distributor_name() {
246 if ( $this->is_from_partner( $this->product ) ) {
247 return 'Themeisle';
248 }
249
250 return $this->product->get_store_name();
251 }
252
253 /**
254 * License price id.
255 *
256 * @return int License plan.
257 */
258 public function get_plan() {
259 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
260 if ( ! isset( $license_data->price_id ) ) {
261 return - 1;
262 }
263
264 return (int) $license_data->price_id;
265 }
266
267 /**
268 * Show the admin notice regarding the license status.
269 *
270 * @return bool Should we show the notice ?
271 */
272 public function show_notice() {
273 if ( ! is_admin() ) {
274 return false;
275 }
276
277 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
278 return false;
279 }
280
281 $status = $this->get_license_status( true );
282 $no_activations_string = apply_filters( $this->product->get_key() . '_lc_no_activations_string', 'No more activations left for %s. You need to upgrade your plan in order to use %s on more websites. If you need assistance, please get in touch with %s staff.' );
283 $no_valid_string = apply_filters( $this->product->get_key() . '_lc_no_valid_string', 'In order to benefit from updates and support for %s, please add your license code from your <a href="%s" target="_blank">purchase history</a> and validate it <a href="%s">here</a>. ' );
284 $expired_license_string = apply_filters( $this->product->get_key() . '_lc_expired_string', 'Your %s\'s License Key has expired. In order to continue receiving support and software updates you must <a href="%s" target="_blank">renew</a> your license key.' );
285 // No activations left for this license.
286 if ( 'valid' != $status && $this->check_activation() ) {
287 ?>
288 <div class="error">
289 <p><strong>
290 <?php
291 echo sprintf(
292 wp_kses_data( $no_activations_string ),
293 esc_attr( $this->product->get_name() ),
294 esc_attr( $this->product->get_name() ),
295 '<a href="' . esc_url( $this->get_api_url() ) . '" target="_blank">' . esc_attr( $this->get_distributor_name() ) . '</a>'
296 );
297 ?>
298 </strong>
299 </p>
300 </div>
301 <?php
302 return false;
303 }
304
305 // Invalid license key.
306 if ( 'active_expired' === $status ) {
307 ?>
308 <div class="error">
309 <p>
310 <strong><?php echo sprintf( wp_kses_data( $expired_license_string ), esc_attr( $this->product->get_name() . ' ' . $this->product->get_type() ), esc_url( $this->get_api_url() . '?license=' . $this->license_key ) ); ?> </strong>
311 </p>
312 </div>
313 <?php
314
315 return false;
316 }
317 // Invalid license key.
318 if ( 'valid' != $status ) {
319 ?>
320 <div class="error">
321 <p>
322 <strong><?php echo sprintf( wp_kses_data( $no_valid_string ), esc_attr( $this->product->get_name() . ' ' . $this->product->get_type() ), esc_url( $this->get_api_url() ), esc_url( admin_url( 'options-general.php' ) . '#' . $this->product->get_key() . '_license' ) ); ?> </strong>
323 </p>
324 </div>
325 <?php
326
327 return false;
328 }
329
330 return true;
331 }
332
333 /**
334 * Check if the license is active or not.
335 *
336 * @return bool
337 */
338 public function check_activation() {
339 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
340 if ( '' === $license_data ) {
341 return false;
342 }
343
344 return isset( $license_data->license ) ? ( 'no_activations_left' == $license_data->license ) : false;
345
346 }
347
348 /**
349 * Check if the license is about to expire in the next month.
350 *
351 * @return bool
352 */
353 public function check_expiration() {
354 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
355 if ( '' === $license_data ) {
356 return false;
357 }
358 if ( ! isset( $license_data->expires ) ) {
359 return false;
360 }
361 if ( strtotime( $license_data->expires ) - time() > 30 * 24 * 3600 ) {
362 return false;
363 }
364
365 return true;
366 }
367
368 /**
369 * Return the renew url from the store used.
370 *
371 * @return string The renew url.
372 */
373 public function renew_url() {
374 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
375 if ( '' === $license_data ) {
376 return $this->get_api_url();
377 }
378 if ( ! isset( $license_data->download_id ) || ! isset( $license_data->key ) ) {
379 return $this->get_api_url();
380 }
381
382 return $this->get_api_url() . '/checkout/?edd_license_key=' . $license_data->key . '&download_id=' . $license_data->download_id;
383 }
384
385 /**
386 * Run the license check call.
387 */
388 public function product_valid() {
389 if ( false !== ( $license = get_transient( $this->product->get_key() . '_license_data' ) ) ) { //phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
390 return;
391 }
392 $license = $this->check_license();
393 set_transient( $this->product->get_key() . '_license_data', $license, 12 * HOUR_IN_SECONDS );
394 update_option( $this->product->get_key() . '_license_data', $license );
395 }
396
397 /**
398 * Check the license status.
399 *
400 * @return object The license data.
401 */
402 public function check_license() {
403 $status = $this->get_license_status();
404 if ( 'not_active' === $status ) {
405 $license_data = new \stdClass();
406 $license_data->license = 'not_active';
407
408 return $license_data;
409 }
410 $license = trim( $this->license_key );
411
412 $response = $this->do_license_process( $license, 'check' );
413
414 if ( is_wp_error( $response ) ) {
415 $license_data = new \stdClass();
416 $license_data->license = 'invalid';
417 } else {
418 $license_data = $response;
419 }
420
421 $license_old = get_option( $this->product->get_key() . '_license_data', '' );
422 if ( 'valid' === $license_old->license && ( $license_data->license !== $license_old->license ) && $this->failed_checks <= self::$max_failed ) {
423 $this->increment_failed_checks();
424
425 return $license_old;
426 }
427
428 if ( ! isset( $license_data->key ) ) {
429 $license_data->key = isset( $license_old->key ) ? $license_old->key : '';
430 }
431 $this->reset_failed_checks();
432
433 return $license_data;
434
435 }
436
437 /**
438 * Do license activation/deactivation.
439 *
440 * @param string $license License key.
441 * @param string $action What do to.
442 *
443 * @return bool|\WP_Error
444 */
445 public function do_license_process( $license, $action = 'toggle' ) {
446 if ( strlen( $license ) < 10 ) {
447 return new \WP_Error( 'themeisle-license-invalid-format', 'Invalid license.' );
448 }
449 $status = $this->get_license_status();
450
451 if ( 'valid' === $status && 'activate' === $action ) {
452 return new \WP_Error( 'themeisle-license-already-active', 'License is already active.' );
453 }
454 if ( 'valid' !== $status && 'deactivate' === $action ) {
455 return new \WP_Error( 'themeisle-license-already-deactivate', 'License not active.' );
456 }
457
458 if ( 'toggle' === $action ) {
459 $action = ( 'valid' !== $status ? ( 'activate' ) : ( 'deactivate' ) );
460 }
461
462 // Call the custom API.
463 if ( 'check' === $action ) {
464 $response = $this->safe_get( sprintf( '%slicense/check/%s/%s/%s/%s', Product::API_URL, rawurlencode( $this->product->get_name() ), $license, rawurlencode( home_url() ), Loader::get_cache_token() ) );
465 } else {
466 $response = wp_remote_post(
467 sprintf( '%slicense/%s/%s/%s', Product::API_URL, $action, rawurlencode( $this->product->get_name() ), $license ),
468 array(
469 'body' => wp_json_encode(
470 array(
471 'url' => rawurlencode( home_url() ),
472 )
473 ),
474 'headers' => array(
475 'Content-Type' => 'application/json',
476 ),
477 )
478 );
479 }
480
481 // make sure the response came back okay.
482 if ( is_wp_error( $response ) ) {
483 return new \WP_Error( 'themeisle-license-500', sprintf( 'ERROR: Failed to connect to the license service. Please try again later. Reason: %s', $response->get_error_message() ) );
484 }
485
486 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
487
488 if ( ! is_object( $license_data ) ) {
489 return new \WP_Error( 'themeisle-license-404', 'ERROR: Failed to validate license. Please try again in one minute.' );
490 }
491 if ( 'check' === $action ) {
492 return $license_data;
493 }
494
495 Loader::clear_cache_token();
496
497 if ( ! isset( $license_data->license ) ) {
498 $license_data->license = 'invalid';
499 }
500
501 if ( ! isset( $license_data->key ) ) {
502 $license_data->key = $license;
503 }
504 if ( 'valid' === $license_data->license ) {
505 $this->reset_failed_checks();
506 }
507
508 if ( 'deactivate' === $action ) {
509
510 delete_option( $this->product->get_key() . '_license_data' );
511 delete_option( $this->product->get_key() . '_license_plan' );
512 delete_transient( $this->product->get_key() . '_license_data' );
513
514 return true;
515 }
516 if ( isset( $license_data->plan ) ) {
517 update_option( $this->product->get_key() . '_license_plan', $license_data->plan );
518 }
519 update_option( $this->product->get_key() . '_license_data', $license_data );
520 set_transient( $this->product->get_key() . '_license_data', $license_data, 12 * HOUR_IN_SECONDS );
521 if ( 'activate' === $action && 'valid' !== $license_data->license ) {
522 return new \WP_Error( 'themeisle-license-invalid', 'ERROR: Invalid license provided.' );
523 }
524
525 // Remove the versions transient upon activation so that newer version for rollback can be acquired.
526 $versions_cache = $this->product->get_cache_key();
527 delete_transient( $versions_cache );
528
529 return true;
530 }
531
532 /**
533 * Reset the failed checks
534 */
535 private function reset_failed_checks() {
536 $this->failed_checks = 1;
537 update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
538 }
539
540 /**
541 * Increment the failed checks.
542 */
543 private function increment_failed_checks() {
544 $this->failed_checks ++;
545 update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
546 }
547
548 /**
549 * Activate the license remotely.
550 */
551 public function process_license() {
552 // listen for our activate button to be clicked.
553 if ( ! isset( $_POST[ $this->product->get_key() . '_btn_trigger' ] ) ) {
554 return;
555 }
556 if ( ! isset( $_POST[ $this->product->get_key() . 'nonce_field' ] )
557 || ! wp_verify_nonce( $_POST[ $this->product->get_key() . 'nonce_field' ], $this->product->get_key() . 'nonce' ) //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
558 ) {
559 return;
560 }
561 if ( ! current_user_can( 'manage_options' ) ) {
562 return;
563 }
564 $license = isset( $_POST[ $this->product->get_key() . '_license' ] )
565 ? sanitize_text_field( $_POST[ $this->product->get_key() . '_license' ] )
566 : '';
567
568 $response = $this->do_license_process( $license, 'toggle' );
569 if ( is_wp_error( $response ) ) {
570 $this->set_error( $response->get_error_message() );
571
572 return;
573 }
574 if ( true === $response ) {
575 $this->set_error( '' );
576 }
577 }
578
579 /**
580 * Set license validation error message.
581 *
582 * @param string $message Error message.
583 */
584 public function set_error( $message = '' ) {
585 set_transient( $this->product->get_key() . 'act_err', $message, MINUTE_IN_SECONDS );
586
587 }
588
589 /**
590 * Load the Themes screen.
591 */
592 public function load_themes_screen() {
593 add_thickbox();
594 add_action( 'admin_notices', array( &$this, 'update_nag' ) );
595 }
596
597 /**
598 * Alter the nag for themes update.
599 */
600 public function update_nag() {
601 $theme = wp_get_theme( $this->product->get_slug() );
602 $api_response = get_transient( $this->product_key );
603 if ( false === $api_response || ! isset( $api_response->new_version ) ) {
604 return;
605 }
606 $update_url = wp_nonce_url( 'update.php?action=upgrade-theme&amp;theme=' . urlencode( $this->product->get_slug() ), 'upgrade-theme_' . $this->product->get_slug() );
607 $update_message = apply_filters( 'themeisle_sdk_license_update_message', 'Updating this theme will lose any customizations you have made. Cancel to stop, OK to update.' );
608 $update_onclick = ' onclick="if ( confirm(\'' . esc_js( $update_message ) . '\') ) {return true;}return false;"';
609 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
610 echo '<div id="update-nag">';
611 printf(
612 '<strong>%1$s %2$s</strong> is available. <a href="%3$s" class="thickbox" title="%4s">Check out what\'s new</a> or <a href="%5$s"%6$s>update now</a>.',
613 esc_attr( $theme->get( 'Name' ) ),
614 esc_attr( $api_response->new_version ),
615 esc_url( sprintf( '%s&TB_iframe=true&amp;width=1024&amp;height=800', $this->product->get_changelog() ) ),
616 esc_attr( $theme->get( 'Name' ) ),
617 esc_url( $update_url ),
618 $update_onclick // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Already escaped.
619 );
620 echo '</div>';
621 echo '<div id="' . esc_attr( $this->product->get_slug() ) . '_changelog" style="display:none;">';
622 echo wp_kses_data( wpautop( $api_response->sections['changelog'] ) );
623 echo '</div>';
624 }
625 }
626
627 /**
628 * Alter update transient.
629 *
630 * @param mixed $value The transient data.
631 *
632 * @return mixed
633 */
634 public function theme_update_transient( $value ) {
635 $update_data = $this->check_for_update();
636 if ( empty( $value ) ) {
637 return $value;
638 }
639
640 if ( ! isset( $value->response ) ) {
641 return $value;
642 }
643
644 if ( ! $update_data ) {
645 return $value;
646 }
647
648 $value->response[ $this->product->get_slug() ] = $update_data;
649 return $value;
650 }
651
652 /**
653 * Check for updates
654 *
655 * @return array|bool Either the update data or false in case of failure.
656 */
657 public function check_for_update() {
658 $update_data = get_transient( $this->product_key );
659
660 if ( false === $update_data ) {
661 $failed = false;
662 $update_data = $this->get_version_data();
663 if ( empty( $update_data ) ) {
664 $failed = true;
665 }
666 // If the response failed, try again in 30 minutes.
667 if ( $failed ) {
668 $data = new \stdClass();
669 $data->new_version = $this->product->get_version();
670 set_transient( $this->product_key, $data, 30 * MINUTE_IN_SECONDS );
671
672 return false;
673 }
674 $update_data->sections = isset( $update_data->sections ) ? maybe_unserialize( $update_data->sections ) : null;
675
676 set_transient( $this->product_key, $update_data, 12 * HOUR_IN_SECONDS );
677 }
678 if ( ! isset( $update_data->new_version ) ) {
679 return false;
680 }
681 if ( version_compare( $this->product->get_version(), $update_data->new_version, '>=' ) ) {
682 return false;
683 }
684
685 return (array) $update_data;
686 }
687
688 /**
689 * Check remote api for latest version.
690 *
691 * @return bool|mixed Update api response.
692 */
693 private function get_version_data() {
694
695 $response = $this->safe_get(
696 sprintf(
697 '%slicense/version/%s/%s/%s/%s',
698 Product::API_URL,
699 rawurlencode( $this->product->get_name() ),
700 ( empty( $this->license_key ) ? 'free' : $this->license_key ),
701 $this->product->get_version(),
702 rawurlencode( home_url() )
703 ),
704 array(
705 'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
706 'sslverify' => false,
707 )
708 );
709 if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
710 return false;
711 }
712 $update_data = json_decode( wp_remote_retrieve_body( $response ) );
713 if ( ! is_object( $update_data ) ) {
714 return false;
715 }
716 if ( isset( $update_data->slug ) ) {
717 $update_data->slug = $this->product->get_slug();
718 }
719 if ( isset( $update_data->icons ) ) {
720 $update_data->icons = (array) $update_data->icons;
721 }
722 if ( isset( $update_data->banners ) ) {
723 $update_data->banners = (array) $update_data->banners;
724 }
725
726 return $update_data;
727 }
728
729 /**
730 * Delete the update transient
731 */
732 public function delete_theme_update_transient() {
733 return delete_transient( $this->product_key );
734 }
735
736 /**
737 * Check for Updates at the defined API endpoint and modify the update array.
738 *
739 * @param array $_transient_data Update array build by WordPress.
740 *
741 * @return mixed Modified update array with custom plugin data.
742 */
743 public function pre_set_site_transient_update_plugins_filter( $_transient_data ) {
744 if ( empty( $_transient_data ) || ! $this->do_check ) {
745 $this->do_check = true;
746
747 return $_transient_data;
748 }
749 $api_response = $this->api_request();
750 if ( false !== $api_response && is_object( $api_response ) && isset( $api_response->new_version ) ) {
751 if ( ! isset( $api_response->plugin ) ) {
752 $api_response->plugin = $this->product->get_slug() . '/' . $this->product->get_file();
753 }
754 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
755 $_transient_data->response[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
756 } else {
757 $_transient_data->no_update[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
758 }
759 }
760
761 return $_transient_data;
762 }
763
764 /**
765 * Calls the API and, if successfull, returns the object delivered by the API.
766 *
767 * @param string $_action The requested action.
768 * @param array $_data Parameters for the API action.
769 *
770 * @return false||object
771 */
772 private function api_request( $_action = '', $_data = '' ) {
773 $update_data = $this->get_version_data();
774 if ( empty( $update_data ) ) {
775 return false;
776 }
777 if ( $update_data && isset( $update_data->sections ) ) {
778 $update_data->sections = maybe_unserialize( $update_data->sections );
779 }
780
781 return $update_data;
782 }
783
784 /**
785 * Updates information on the "View version x.x details" page with custom data.
786 *
787 * @param mixed $_data Plugin data.
788 * @param string $_action Action to send.
789 * @param object $_args Arguments to use.
790 *
791 * @return object $_data
792 */
793 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
794 if ( ( 'plugin_information' !== $_action ) || ! isset( $_args->slug ) || ( $_args->slug !== $this->product->get_slug() ) ) {
795 return $_data;
796 }
797 $api_response = $this->api_request();
798 if ( false !== $api_response ) {
799 $_data = $api_response;
800 }
801
802 return $_data;
803 }
804
805 /**
806 * Disable SSL verification in order to prevent download update failures.
807 *
808 * @param array $args Http args.
809 * @param string $url Url to check.
810 *
811 * @return array $array
812 */
813 public function http_request_args( $args, $url ) {
814 // If it is an https request and we are performing a package download, disable ssl verification.
815 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
816 $args['sslverify'] = false;
817 }
818
819 return $args;
820 }
821
822 /**
823 * Check if we should load the module for this product.
824 *
825 * @param Product $product Product data.
826 *
827 * @return bool Should we load the module?
828 */
829 public function can_load( $product ) {
830
831 if ( $product->is_wordpress_available() ) {
832 return false;
833 }
834
835 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
836
837 }
838
839 /**
840 * Load module logic.
841 *
842 * @param Product $product Product to load the module for.
843 *
844 * @return Licenser Module object.
845 */
846 public function load( $product ) {
847 $this->product = $product;
848
849 $this->product_key = $this->product->get_key() . '-update-response';
850
851 $this->license_key = $this->product->get_license();
852 if ( $this->product->requires_license() ) {
853 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
854 $this->register_license_hooks();
855 }
856
857 $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
858
859 if ( false !== $namespace ) {
860 $this->namespace = $namespace;
861 add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
862 add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
863 add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
864 add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
865 if ( defined( 'WP_CLI' ) && WP_CLI ) {
866 \WP_CLI::add_command( $namespace . ' activate', [ $this, 'cli_activate' ] );
867 \WP_CLI::add_command( $namespace . ' deactivate', [ $this, 'cli_deactivate' ] );
868 \WP_CLI::add_command( $namespace . ' is-active', [ $this, 'cli_is_active' ] );
869 }
870 }
871
872 add_action( 'admin_head', [ $this, 'auto_activate' ] );
873 if ( $this->product->is_plugin() ) {
874 add_filter(
875 'pre_set_site_transient_update_plugins',
876 [
877 $this,
878 'pre_set_site_transient_update_plugins_filter',
879 ]
880 );
881 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
882 add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
883
884 return $this;
885 }
886 if ( $this->product->is_theme() ) {
887 add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
888 add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
889 add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
890 add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
891 add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
892 add_filter( 'http_request_args', array( $this, 'disable_wporg_update' ), 5, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
893
894 return $this;
895
896 }
897
898 return $this;
899 }
900
901 /**
902 * Register license fields for the products.
903 */
904 public function register_license_hooks() {
905 add_action( 'admin_init', array( $this, 'register_settings' ) );
906 add_action( 'admin_init', array( $this, 'process_license' ) );
907 add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
908 add_action( 'admin_notices', array( $this, 'show_notice' ) );
909 add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
910 }
911
912 /**
913 * Check license on filesystem.
914 *
915 * @return mixed License key.
916 */
917 public function get_file_license() {
918
919 $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
920
921 global $wp_filesystem;
922 if ( ! is_file( $license_file ) ) {
923 return false;
924 }
925
926 require_once ABSPATH . '/wp-admin/includes/file.php';
927 \WP_Filesystem();
928 $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
929 if ( ! is_object( $content ) ) {
930 return false;
931 }
932 if ( ! isset( $content->key ) ) {
933 return false;
934 }
935 return $content->key;
936 }
937 /**
938 * Run license activation on plugin activate.
939 */
940 public function auto_activate() {
941 $status = $this->get_license_status();
942 if ( 'not_active' !== $status ) {
943 return false;
944 }
945
946 if ( ! empty( $this->namespace ) ) {
947 $license_key = apply_filters( 'product_' . $this->namespace . '_license_key_constant', '' );
948 }
949
950 if ( empty( $license_key ) ) {
951 $license_key = $this->get_file_license();
952 }
953 if ( empty( $license_key ) ) {
954 return;
955 }
956
957
958 $this->license_local = $license_key;
959 $lock_key = $this->product->get_key() . '_autoactivated';
960
961 if ( 'yes' === get_option( $lock_key, '' ) ) {
962 return;
963 }
964 if ( 'yes' === get_transient( $lock_key ) ) {
965 return;
966 }
967 $response = $this->do_license_process( $license_key, 'activate' );
968
969 set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
970
971 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
972 return;
973 }
974
975 if ( true === $response ) {
976 add_action( 'admin_notices', [ $this, 'autoactivate_notice' ] );
977 }
978 }
979
980 /**
981 * Show auto-activate notice.
982 */
983 public function autoactivate_notice() {
984 ?>
985 <div class="notice notice-success is-dismissible">
986 <p><?php echo sprintf( '<strong>%s</strong> has been successfully activated using <strong>%s</strong> license !', esc_attr( $this->product->get_name() ), esc_attr( str_repeat( '*', 20 ) . substr( $this->license_local, - 10 ) ) ); ?></p>
987 </div>
988 <?php
989 }
990
991 /**
992 * Activate product license on this site.
993 *
994 * ## OPTIONS
995 *
996 * @param array $args Command args.
997 *
998 * [<license-key>]
999 * : Product license key.
1000 */
1001 public function cli_activate( $args ) {
1002 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1003 $response = $this->do_license_process( $license_key, 'activate' );
1004 if ( true !== $response ) {
1005 \WP_CLI::error( $response->get_error_message() );
1006
1007 return;
1008 }
1009
1010 \WP_CLI::success( 'Product successfully activated.' );
1011 }
1012
1013 /**
1014 * Deactivate product license on this site.
1015 *
1016 * @param array $args Command args.
1017 *
1018 * ## OPTIONS
1019 *
1020 * [<license-key>]
1021 * : Product license key.
1022 */
1023 public function cli_deactivate( $args ) {
1024 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1025 $response = $this->do_license_process( $license_key, 'deactivate' );
1026 if ( true !== $response ) {
1027 \WP_CLI::error( $response->get_error_message() );
1028
1029 return;
1030 }
1031
1032 \WP_CLI::success( 'Product successfully deactivated.' );
1033 }
1034
1035 /**
1036 * Checks if product has license activated.
1037 *
1038 * @param array $args Command args.
1039 *
1040 * @subcommand is-active
1041 */
1042 public function cli_is_active( $args ) {
1043
1044 $status = $this->get_license_status();
1045 if ( 'valid' === $status ) {
1046 \WP_CLI::halt( 0 );
1047
1048 return;
1049 }
1050
1051 \WP_CLI::halt( 1 );
1052 }
1053 }
1054