PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.10
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.10
4.0.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 All 149 releases
visualizer / vendor / codeinwp / themeisle-sdk / src / Modules / Licenser.php

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

1,194 lines 34.7 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', Loader::$labels['licenser']['activate'] );
142 $deactivate_string = apply_filters( $this->product->get_key() . '_lc_deactivate_string', Loader::$labels['licenser']['deactivate'] );
143 $valid_string = apply_filters( $this->product->get_key() . '_lc_valid_string', Loader::$labels['licenser']['valid'] );
144 $invalid_string = apply_filters( $this->product->get_key() . '_lc_invalid_string', Loader::$labels['licenser']['invalid'] );
145 $license_message = apply_filters( $this->product->get_key() . '_lc_license_message', Loader::$labels['licenser']['notice'] );
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', Loader::$labels['licenser']['no_activations'] );
382 $no_valid_string = apply_filters( $this->product->get_key() . '_lc_no_valid_string', sprintf( Loader::$labels['licenser']['inactive'], '%s', '<a href="%s" target="_blank">', '</a>', '<a href="%s">', '</a>' ) );
383 $expired_license_string = apply_filters( $this->product->get_key() . '_lc_expired_string', sprintf( Loader::$labels['licenser']['expired'], '%s', '<a href="%s" target="_blank">', '</a>' ) );
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', Loader::$labels['licenser']['invalid_msg'] );
547 }
548 $status = $this->get_license_status();
549
550 if ( 'valid' === $status && 'activate' === $action ) {
551 return new \WP_Error( 'themeisle-license-already-active', Loader::$labels['licenser']['already_active'] );
552 }
553 if ( 'valid' !== $status && 'deactivate' === $action ) {
554 return new \WP_Error( 'themeisle-license-already-deactivate', Loader::$labels['licenser']['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( Loader::$labels['licenser']['error_notice'], $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', Loader::$labels['licenser']['error_notice2'] );
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', Loader::$labels['licenser']['error_invalid'] );
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', Loader::$labels['licenser']['update_license'] );
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 esc_html( Loader::$labels['licenser']['notice_update'] ),
712 '<strong>' . esc_attr( $theme->get( 'Name' ) ) . ' ' . esc_attr( $api_response->new_version ) . '</strong>',
713 '<a href="' . esc_url( sprintf( '%s&TB_iframe=true&amp;width=1024&amp;height=800', $this->product->get_changelog() ) ) . '" class="thickbox" title="' . esc_attr( $theme->get( 'Name' ) ) . '">',
714 '</a>',
715 '<a href="' . esc_url( $update_url ) . '" ' . $update_onclick . '>' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Already escaped.
716 );
717 echo '</div>';
718 echo '<div id="' . esc_attr( $this->product->get_slug() ) . '_changelog" style="display:none;">';
719 echo wp_kses_data( wpautop( $api_response->sections['changelog'] ) );
720 echo '</div>';
721 }
722 }
723
724 /**
725 * Alter update transient.
726 *
727 * @param mixed $value The transient data.
728 *
729 * @return mixed
730 */
731 public function theme_update_transient( $value ) {
732 $update_data = $this->check_for_update();
733 if ( empty( $value ) ) {
734 return $value;
735 }
736
737 if ( ! isset( $value->response ) ) {
738 return $value;
739 }
740
741 if ( ! $update_data ) {
742 return $value;
743 }
744
745 $value->response[ $this->product->get_slug() ] = $update_data;
746
747 return $value;
748 }
749
750 /**
751 * Check for updates
752 *
753 * @return array|bool Either the update data or false in case of failure.
754 */
755 public function check_for_update() {
756 $update_data = get_transient( $this->product_key );
757
758 if ( false === $update_data ) {
759 $failed = false;
760 $update_data = $this->get_version_data();
761 if ( empty( $update_data ) ) {
762 $failed = true;
763 }
764 // If the response failed, try again in 30 minutes.
765 if ( $failed ) {
766 $data = new \stdClass();
767 $data->new_version = $this->product->get_version();
768 set_transient( $this->product_key, $data, 30 * MINUTE_IN_SECONDS );
769
770 return false;
771 }
772 $update_data->sections = isset( $update_data->sections ) ? maybe_unserialize( $update_data->sections ) : null;
773
774 set_transient( $this->product_key, $update_data, 12 * HOUR_IN_SECONDS );
775 }
776 if ( ! isset( $update_data->new_version ) ) {
777 return false;
778 }
779 if ( version_compare( $this->product->get_version(), $update_data->new_version, '>=' ) ) {
780 return false;
781 }
782
783 return (array) $update_data;
784 }
785
786 /**
787 * Check remote api for latest version.
788 *
789 * @return bool|mixed Update api response.
790 */
791 private function get_version_data() {
792
793 $response = $this->safe_get(
794 sprintf(
795 '%slicense/version/%s/%s/%s/%s',
796 Product::API_URL,
797 rawurlencode( $this->product->get_name() ),
798 ( empty( $this->license_key ) ? 'free' : $this->license_key ),
799 $this->product->get_version(),
800 rawurlencode( home_url() )
801 ),
802 array(
803 'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
804 'sslverify' => false,
805 )
806 );
807 if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
808 return false;
809 }
810 $update_data = json_decode( wp_remote_retrieve_body( $response ) );
811 if ( ! is_object( $update_data ) ) {
812 return false;
813 }
814 if ( isset( $update_data->slug ) ) {
815 $update_data->slug = $this->product->get_slug();
816 }
817 if ( isset( $update_data->icons ) ) {
818 $update_data->icons = (array) $update_data->icons;
819 }
820 if ( isset( $update_data->banners ) ) {
821 $update_data->banners = (array) $update_data->banners;
822 }
823
824 return $update_data;
825 }
826
827 /**
828 * Delete the update transient
829 */
830 public function delete_theme_update_transient() {
831 return delete_transient( $this->product_key );
832 }
833
834 /**
835 * Check for Updates at the defined API endpoint and modify the update array.
836 *
837 * @param array $_transient_data Update array build by WordPress.
838 *
839 * @return mixed Modified update array with custom plugin data.
840 */
841 public function pre_set_site_transient_update_plugins_filter( $_transient_data ) {
842 if ( empty( $_transient_data ) || ! $this->do_check ) {
843 $this->do_check = true;
844
845 return $_transient_data;
846 }
847 $api_response = $this->api_request();
848 if ( false !== $api_response && is_object( $api_response ) && isset( $api_response->new_version ) ) {
849 if ( ! isset( $api_response->plugin ) ) {
850 $api_response->plugin = $this->product->get_slug() . '/' . $this->product->get_file();
851 }
852 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
853 $_transient_data->response[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
854 } else {
855 $_transient_data->no_update[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
856 }
857 }
858
859 return $_transient_data;
860 }
861
862 /**
863 * Calls the API and, if successfull, returns the object delivered by the API.
864 *
865 * @param string $_action The requested action.
866 * @param array $_data Parameters for the API action.
867 *
868 * @return false||object
869 */
870 private function api_request( $_action = '', $_data = '' ) {
871 $update_data = $this->get_version_data();
872 if ( empty( $update_data ) ) {
873 return false;
874 }
875 if ( $update_data && isset( $update_data->sections ) ) {
876 $update_data->sections = maybe_unserialize( $update_data->sections );
877 }
878
879 return $update_data;
880 }
881
882 /**
883 * Updates information on the "View version x.x details" page with custom data.
884 *
885 * @param mixed $_data Plugin data.
886 * @param string $_action Action to send.
887 * @param object $_args Arguments to use.
888 *
889 * @return object $_data
890 */
891 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
892 if ( ( 'plugin_information' !== $_action ) || ! isset( $_args->slug ) || ( $_args->slug !== $this->product->get_slug() ) ) {
893 return $_data;
894 }
895 $api_response = $this->api_request();
896 if ( false !== $api_response ) {
897 $_data = $api_response;
898 }
899
900 return $_data;
901 }
902
903 /**
904 * Disable SSL verification in order to prevent download update failures.
905 *
906 * @param array $args Http args.
907 * @param string $url Url to check.
908 *
909 * @return array $array
910 */
911 public function http_request_args( $args, $url ) {
912 // If it is an https request and we are performing a package download, disable ssl verification.
913 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
914 $args['sslverify'] = false;
915 }
916
917 return $args;
918 }
919
920 /**
921 * Check if we should load the module for this product.
922 *
923 * @param Product $product Product data.
924 *
925 * @return bool Should we load the module?
926 */
927 public function can_load( $product ) {
928
929 if ( ! $product->requires_license() ) {
930 return false;
931 }
932
933 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
934
935 }
936
937 /**
938 * Load module logic.
939 *
940 * @param Product $product Product to load the module for.
941 *
942 * @return Licenser Module object.
943 */
944 public function load( $product ) {
945 $this->product = $product;
946
947 $this->product_key = $this->product->get_key() . '-update-response';
948
949 $this->license_key = $this->product->get_license();
950 if ( $this->product->requires_license() ) {
951 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
952 $this->register_license_hooks();
953 }
954 if ( ! self::$globals_loaded ) {
955 add_filter( 'themeisle_sdk_license/status', [ __CLASS__, 'status' ], 999, 1 );
956 add_filter( 'themeisle_sdk_license/is-valid', [ __CLASS__, 'is_valid' ], 999, 1 );
957 add_filter( 'themeisle_sdk_license/plan', [ __CLASS__, 'plan' ], 999, 1 );
958 add_filter( 'themeisle_sdk_license/key', [ __CLASS__, 'key' ], 999, 1 );
959 $globals_loaded = true;
960 }
961 $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
962
963 if ( false !== $namespace ) {
964 $this->namespace = $namespace;
965 add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
966 add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
967 add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
968 add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
969 if ( defined( 'WP_CLI' ) && WP_CLI ) {
970 \WP_CLI::add_command( $namespace . ' activate', [ $this, 'cli_activate' ] );
971 \WP_CLI::add_command( $namespace . ' deactivate', [ $this, 'cli_deactivate' ] );
972 \WP_CLI::add_command( $namespace . ' is-active', [ $this, 'cli_is_active' ] );
973 }
974 }
975
976 add_action( 'admin_head', [ $this, 'auto_activate' ] );
977 if ( $this->product->is_plugin() ) {
978 if ( ! $product->is_wordpress_available() ) {
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( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
988 'http_request_args',
989 array(
990 $this,
991 'http_request_args',
992 ),
993 10,
994 2
995 );
996 if ( ! self::is_valid( $product->get_basefile() ) ) {
997 add_filter(
998 'plugin_action_links_' . plugin_basename( $product->get_basefile() ),
999 function ( $actions ) {
1000 if ( $this->get_license_status( true ) !== self::STATUS_ACTIVE_EXPIRED ) {
1001 return $actions;
1002 }
1003 $new_actions['deactivate'] = $actions['deactivate'];
1004 $new_actions['renew_link'] = '<a style="color:#d63638" href="' . esc_url( $this->renew_url() ) . '" target="_blank" rel="external noopener noreferrer">' . esc_html( Loader::$labels['licenser']['renew_cta'] ) . '</a>';
1005
1006 return $new_actions;
1007 }
1008 );
1009 }
1010 }
1011
1012 return $this;
1013 }
1014 if ( $this->product->is_theme() ) {
1015 if ( ! $product->is_wordpress_available() ) {
1016 add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
1017 add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
1018 add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
1019 add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
1020 add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
1021 add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
1022 'http_request_args',
1023 array(
1024 $this,
1025 'disable_wporg_update',
1026 ),
1027 5,
1028 2
1029 );
1030 }
1031
1032 return $this;
1033
1034 }
1035
1036 return $this;
1037 }
1038
1039 /**
1040 * Register license fields for the products.
1041 */
1042 public function register_license_hooks() {
1043 add_action( 'admin_init', array( $this, 'register_settings' ) );
1044 add_action( 'admin_init', array( $this, 'process_license' ) );
1045 add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
1046 add_action( 'admin_notices', array( $this, 'show_notice' ) );
1047 add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
1048 }
1049
1050 /**
1051 * Check license on filesystem.
1052 *
1053 * @return mixed License key.
1054 */
1055 public function get_file_license() {
1056
1057 $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
1058
1059 global $wp_filesystem;
1060 if ( ! is_file( $license_file ) ) {
1061 return false;
1062 }
1063
1064 require_once ABSPATH . '/wp-admin/includes/file.php';
1065 \WP_Filesystem();
1066 $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
1067 if ( ! is_object( $content ) ) {
1068 return false;
1069 }
1070 if ( ! isset( $content->key ) ) {
1071 return false;
1072 }
1073
1074 return $content->key;
1075 }
1076
1077 /**
1078 * Run license activation on plugin activate.
1079 */
1080 public function auto_activate() {
1081 $status = $this->get_license_status();
1082 if ( 'not_active' !== $status ) {
1083 return false;
1084 }
1085
1086 if ( ! empty( $this->namespace ) ) {
1087 $license_key = apply_filters( 'product_' . $this->namespace . '_license_key_constant', '' );
1088 }
1089
1090 if ( empty( $license_key ) ) {
1091 $license_key = $this->get_file_license();
1092 }
1093 if ( empty( $license_key ) ) {
1094 return;
1095 }
1096
1097
1098 $this->license_local = $license_key;
1099 $lock_key = $this->product->get_key() . '_autoactivated';
1100
1101 if ( 'yes' === get_option( $lock_key, '' ) ) {
1102 return;
1103 }
1104 if ( 'yes' === get_transient( $lock_key ) ) {
1105 return;
1106 }
1107 $response = $this->do_license_process( $license_key, 'activate' );
1108
1109 set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
1110
1111 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
1112 return;
1113 }
1114
1115 if ( true === $response ) {
1116 add_action( 'admin_notices', [ $this, 'autoactivate_notice' ] );
1117 }
1118 }
1119
1120 /**
1121 * Show auto-activate notice.
1122 */
1123 public function autoactivate_notice() {
1124 ?>
1125 <div class="notice notice-success is-dismissible">
1126 <p><?php echo sprintf( esc_html( Loader::$labels['licenser']['autoactivate_notice'] ), '<strong>' . esc_attr( $this->product->get_name() ) . '</strong>', '<strong>' . esc_attr( str_repeat( '*', 20 ) . substr( $this->license_local, - 10 ) ) . '</strong>' ); ?></p>
1127 </div>
1128 <?php
1129 }
1130
1131 /**
1132 * Activate product license on this site.
1133 *
1134 * ## OPTIONS
1135 *
1136 * @param array $args Command args.
1137 *
1138 * [<license-key>]
1139 * : Product license key.
1140 */
1141 public function cli_activate( $args ) {
1142 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1143 $response = $this->do_license_process( $license_key, 'activate' );
1144 if ( true !== $response ) {
1145 \WP_CLI::error( $response->get_error_message() );
1146
1147 return;
1148 }
1149
1150 \WP_CLI::success( 'Product successfully activated.' );
1151 }
1152
1153 /**
1154 * Deactivate product license on this site.
1155 *
1156 * @param array $args Command args.
1157 *
1158 * ## OPTIONS
1159 *
1160 * [<license-key>]
1161 * : Product license key.
1162 */
1163 public function cli_deactivate( $args ) {
1164 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1165 $response = $this->do_license_process( $license_key, 'deactivate' );
1166 if ( true !== $response ) {
1167 \WP_CLI::error( $response->get_error_message() );
1168
1169 return;
1170 }
1171
1172 \WP_CLI::success( 'Product successfully deactivated.' );
1173 }
1174
1175 /**
1176 * Checks if product has license activated.
1177 *
1178 * @param array $args Command args.
1179 *
1180 * @subcommand is-active
1181 */
1182 public function cli_is_active( $args ) {
1183
1184 $status = $this->get_license_status();
1185 if ( 'valid' === $status ) {
1186 \WP_CLI::halt( 0 );
1187
1188 return;
1189 }
1190
1191 \WP_CLI::halt( 1 );
1192 }
1193 }
1194