PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.8
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.10.8, at vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php

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