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

1,042 lines 30.9 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 = 5;
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 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 = 'valid';
417 } else {
418 $license_data = $response;
419 }
420
421 $license_old = get_option( $this->product->get_key() . '_license_data', '' );
422
423 if ( 'valid' === $license_old->license && ( $license_data->license !== $license_old->license ) ) {
424 $this->increment_failed_checks();
425 } else {
426 $this->reset_failed_checks();
427 }
428
429 if ( $this->failed_checks <= self::$max_failed ) {
430 return $license_old;
431 }
432
433 if ( ! isset( $license_data->key ) ) {
434 $license_data->key = isset( $license_old->key ) ? $license_old->key : '';
435 }
436
437 return $license_data;
438
439 }
440
441 /**
442 * Do license activation/deactivation.
443 *
444 * @param string $license License key.
445 * @param string $action What do to.
446 *
447 * @return bool|\WP_Error
448 */
449 public function do_license_process( $license, $action = 'toggle' ) {
450 if ( strlen( $license ) < 10 ) {
451 return new \WP_Error( 'themeisle-license-invalid-format', 'Invalid license.' );
452 }
453 $status = $this->get_license_status();
454
455 if ( 'valid' === $status && 'activate' === $action ) {
456 return new \WP_Error( 'themeisle-license-already-active', 'License is already active.' );
457 }
458 if ( 'valid' !== $status && 'deactivate' === $action ) {
459 return new \WP_Error( 'themeisle-license-already-deactivate', 'License not active.' );
460 }
461
462 if ( 'toggle' === $action ) {
463 $action = ( 'valid' !== $status ? ( 'activate' ) : ( 'deactivate' ) );
464 }
465
466 // Call the custom API.
467 if ( 'check' === $action ) {
468 $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() ) );
469 } else {
470 $response = wp_remote_post(
471 sprintf( '%slicense/%s/%s/%s', Product::API_URL, $action, rawurlencode( $this->product->get_name() ), $license ),
472 array(
473 'body' => wp_json_encode(
474 array(
475 'url' => rawurlencode( home_url() ),
476 )
477 ),
478 'headers' => array(
479 'Content-Type' => 'application/json',
480 ),
481 )
482 );
483 }
484
485 // make sure the response came back okay.
486 if ( is_wp_error( $response ) ) {
487 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() ) );
488 }
489
490 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
491
492 if ( ! is_object( $license_data ) ) {
493 return new \WP_Error( 'themeisle-license-404', 'ERROR: Failed to validate license. Please try again in one minute.' );
494 }
495 if ( 'check' === $action ) {
496 return $license_data;
497 }
498
499 Loader::clear_cache_token();
500
501 if ( ! isset( $license_data->license ) ) {
502 $license_data->license = 'invalid';
503 }
504
505 if ( ! isset( $license_data->key ) ) {
506 $license_data->key = $license;
507 }
508 if ( 'valid' === $license_data->license ) {
509 $this->reset_failed_checks();
510 }
511
512 if ( 'deactivate' === $action ) {
513
514 delete_option( $this->product->get_key() . '_license_data' );
515 delete_option( $this->product->get_key() . '_license_plan' );
516 delete_transient( $this->product->get_key() . '_license_data' );
517
518 return true;
519 }
520 if ( isset( $license_data->plan ) ) {
521 update_option( $this->product->get_key() . '_license_plan', $license_data->plan );
522 }
523 update_option( $this->product->get_key() . '_license_data', $license_data );
524 set_transient( $this->product->get_key() . '_license_data', $license_data, 12 * HOUR_IN_SECONDS );
525 if ( 'activate' === $action && 'valid' !== $license_data->license ) {
526 return new \WP_Error( 'themeisle-license-invalid', 'ERROR: Invalid license provided.' );
527 }
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 ( $update_data ) {
637 $value->response[ $this->product->get_slug() ] = $update_data;
638 }
639
640 return $value;
641 }
642
643 /**
644 * Check for updates
645 *
646 * @return array|bool Either the update data or false in case of failure.
647 */
648 public function check_for_update() {
649 $update_data = get_transient( $this->product_key );
650
651 if ( false === $update_data ) {
652 $failed = false;
653 $update_data = $this->get_version_data();
654 if ( empty( $update_data ) ) {
655 $failed = true;
656 }
657 // If the response failed, try again in 30 minutes.
658 if ( $failed ) {
659 $data = new \stdClass();
660 $data->new_version = $this->product->get_version();
661 set_transient( $this->product_key, $data, 30 * MINUTE_IN_SECONDS );
662
663 return false;
664 }
665 $update_data->sections = isset( $update_data->sections ) ? maybe_unserialize( $update_data->sections ) : null;
666
667 set_transient( $this->product_key, $update_data, 12 * HOUR_IN_SECONDS );
668 }
669 if ( ! isset( $update_data->new_version ) ) {
670 return false;
671 }
672 if ( version_compare( $this->product->get_version(), $update_data->new_version, '>=' ) ) {
673 return false;
674 }
675
676 return (array) $update_data;
677 }
678
679 /**
680 * Check remote api for latest version.
681 *
682 * @return bool|mixed Update api response.
683 */
684 private function get_version_data() {
685
686 $response = $this->safe_get(
687 sprintf(
688 '%slicense/version/%s/%s/%s/%s',
689 Product::API_URL,
690 rawurlencode( $this->product->get_name() ),
691 ( empty( $this->license_key ) ? 'free' : $this->license_key ),
692 $this->product->get_version(),
693 rawurlencode( home_url() )
694 ),
695 array(
696 'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
697 'sslverify' => false,
698 )
699 );
700 if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
701 return false;
702 }
703 $update_data = json_decode( wp_remote_retrieve_body( $response ) );
704 if ( ! is_object( $update_data ) ) {
705 return false;
706 }
707 if ( isset( $update_data->slug ) ) {
708 $update_data->slug = $this->product->get_slug();
709 }
710 if ( isset( $update_data->icons ) ) {
711 $update_data->icons = (array) $update_data->icons;
712 }
713 if ( isset( $update_data->banners ) ) {
714 $update_data->banners = (array) $update_data->banners;
715 }
716
717 return $update_data;
718 }
719
720 /**
721 * Delete the update transient
722 */
723 public function delete_theme_update_transient() {
724 return delete_transient( $this->product_key );
725 }
726
727 /**
728 * Check for Updates at the defined API endpoint and modify the update array.
729 *
730 * @param array $_transient_data Update array build by WordPress.
731 *
732 * @return mixed Modified update array with custom plugin data.
733 */
734 public function pre_set_site_transient_update_plugins_filter( $_transient_data ) {
735 if ( empty( $_transient_data ) || ! $this->do_check ) {
736 $this->do_check = true;
737
738 return $_transient_data;
739 }
740 $api_response = $this->api_request();
741 if ( false !== $api_response && is_object( $api_response ) && isset( $api_response->new_version ) ) {
742 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
743 $_transient_data->response[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
744 } else {
745 $_transient_data->no_update[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
746 }
747 }
748
749 return $_transient_data;
750 }
751
752 /**
753 * Calls the API and, if successfull, returns the object delivered by the API.
754 *
755 * @param string $_action The requested action.
756 * @param array $_data Parameters for the API action.
757 *
758 * @return false||object
759 */
760 private function api_request( $_action = '', $_data = '' ) {
761 $update_data = $this->get_version_data();
762 if ( empty( $update_data ) ) {
763 return false;
764 }
765 if ( $update_data && isset( $update_data->sections ) ) {
766 $update_data->sections = maybe_unserialize( $update_data->sections );
767 }
768
769 return $update_data;
770 }
771
772 /**
773 * Updates information on the "View version x.x details" page with custom data.
774 *
775 * @param mixed $_data Plugin data.
776 * @param string $_action Action to send.
777 * @param object $_args Arguments to use.
778 *
779 * @return object $_data
780 */
781 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
782 if ( ( 'plugin_information' !== $_action ) || ! isset( $_args->slug ) || ( $_args->slug !== $this->product->get_slug() ) ) {
783 return $_data;
784 }
785 $api_response = $this->api_request();
786 if ( false !== $api_response ) {
787 $_data = $api_response;
788 }
789
790 return $_data;
791 }
792
793 /**
794 * Disable SSL verification in order to prevent download update failures.
795 *
796 * @param array $args Http args.
797 * @param string $url Url to check.
798 *
799 * @return array $array
800 */
801 public function http_request_args( $args, $url ) {
802 // If it is an https request and we are performing a package download, disable ssl verification.
803 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
804 $args['sslverify'] = false;
805 }
806
807 return $args;
808 }
809
810 /**
811 * Check if we should load the module for this product.
812 *
813 * @param Product $product Product data.
814 *
815 * @return bool Should we load the module?
816 */
817 public function can_load( $product ) {
818
819 if ( $product->is_wordpress_available() ) {
820 return false;
821 }
822
823 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
824
825 }
826
827 /**
828 * Load module logic.
829 *
830 * @param Product $product Product to load the module for.
831 *
832 * @return Licenser Module object.
833 */
834 public function load( $product ) {
835 $this->product = $product;
836
837 $this->product_key = $this->product->get_key() . '-update-response';
838
839 $this->license_key = $this->product->get_license();
840 if ( $this->product->requires_license() ) {
841 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
842 $this->register_license_hooks();
843 }
844
845 $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
846
847 if ( false !== $namespace ) {
848 $this->namespace = $namespace;
849 add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
850 add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
851 add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
852 add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
853 if ( defined( 'WP_CLI' ) && WP_CLI ) {
854 \WP_CLI::add_command( $namespace . ' activate', [ $this, 'cli_activate' ] );
855 \WP_CLI::add_command( $namespace . ' deactivate', [ $this, 'cli_deactivate' ] );
856 \WP_CLI::add_command( $namespace . ' is-active', [ $this, 'cli_is_active' ] );
857 }
858 }
859
860 add_action( 'admin_head', [ $this, 'auto_activate' ] );
861 if ( $this->product->is_plugin() ) {
862 add_filter(
863 'pre_set_site_transient_update_plugins',
864 [
865 $this,
866 'pre_set_site_transient_update_plugins_filter',
867 ]
868 );
869 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
870 add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
871
872 return $this;
873 }
874 if ( $this->product->is_theme() ) {
875 add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
876 add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
877 add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
878 add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
879 add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
880 add_filter( 'http_request_args', array( $this, 'disable_wporg_update' ), 5, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
881
882 return $this;
883
884 }
885
886 return $this;
887 }
888
889 /**
890 * Register license fields for the products.
891 */
892 public function register_license_hooks() {
893 add_action( 'admin_init', array( $this, 'register_settings' ) );
894 add_action( 'admin_init', array( $this, 'process_license' ) );
895 add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
896 add_action( 'admin_notices', array( $this, 'show_notice' ) );
897 add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
898 }
899
900 /**
901 * Check license on filesystem.
902 *
903 * @return mixed License key.
904 */
905 public function get_file_license() {
906
907 $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
908
909 global $wp_filesystem;
910 if ( ! is_file( $license_file ) ) {
911 return false;
912 }
913
914 require_once ABSPATH . '/wp-admin/includes/file.php';
915 \WP_Filesystem();
916 $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
917 if ( ! is_object( $content ) ) {
918 return false;
919 }
920 if ( ! isset( $content->key ) ) {
921 return false;
922 }
923 return $content->key;
924 }
925 /**
926 * Run license activation on plugin activate.
927 */
928 public function auto_activate() {
929 $status = $this->get_license_status();
930 if ( 'not_active' !== $status ) {
931 return false;
932 }
933
934 if ( ! empty( $this->namespace ) ) {
935 $license_key = apply_filters( 'product_' . $this->namespace . '_license_key_constant', '' );
936 }
937
938 if ( empty( $license_key ) ) {
939 $license_key = $this->get_file_license();
940 }
941 if ( empty( $license_key ) ) {
942 return;
943 }
944
945
946 $this->license_local = $license_key;
947 $lock_key = $this->product->get_key() . '_autoactivated';
948
949 if ( 'yes' === get_option( $lock_key, '' ) ) {
950 return;
951 }
952 if ( 'yes' === get_transient( $lock_key ) ) {
953 return;
954 }
955 $response = $this->do_license_process( $license_key, 'activate' );
956
957 set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
958
959 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
960 return;
961 }
962
963 if ( true === $response ) {
964 add_action( 'admin_notices', [ $this, 'autoactivate_notice' ] );
965 }
966 }
967
968 /**
969 * Show auto-activate notice.
970 */
971 public function autoactivate_notice() {
972 ?>
973 <div class="notice notice-success is-dismissible">
974 <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>
975 </div>
976 <?php
977 }
978
979 /**
980 * Activate product license on this site.
981 *
982 * ## OPTIONS
983 *
984 * @param array $args Command args.
985 *
986 * [<license-key>]
987 * : Product license key.
988 */
989 public function cli_activate( $args ) {
990 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
991 $response = $this->do_license_process( $license_key, 'activate' );
992 if ( true !== $response ) {
993 \WP_CLI::error( $response->get_error_message() );
994
995 return;
996 }
997
998 \WP_CLI::success( 'Product successfully activated.' );
999 }
1000
1001 /**
1002 * Deactivate product license on this site.
1003 *
1004 * @param array $args Command args.
1005 *
1006 * ## OPTIONS
1007 *
1008 * [<license-key>]
1009 * : Product license key.
1010 */
1011 public function cli_deactivate( $args ) {
1012 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1013 $response = $this->do_license_process( $license_key, 'deactivate' );
1014 if ( true !== $response ) {
1015 \WP_CLI::error( $response->get_error_message() );
1016
1017 return;
1018 }
1019
1020 \WP_CLI::success( 'Product successfully deactivated.' );
1021 }
1022
1023 /**
1024 * Checks if product has license activated.
1025 *
1026 * @param array $args Command args.
1027 *
1028 * @subcommand is-active
1029 */
1030 public function cli_is_active( $args ) {
1031
1032 $status = $this->get_license_status();
1033 if ( 'valid' === $status ) {
1034 \WP_CLI::halt( 0 );
1035
1036 return;
1037 }
1038
1039 \WP_CLI::halt( 1 );
1040 }
1041 }
1042