PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.1.3
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.1.3
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / vendor / codeinwp / themeisle-sdk / src / Modules / Licenser.php

Licenser.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 3.1.3, at vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php

1,045 lines 31.1 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 ( ! isset( $api_response->plugin ) ) {
743 $api_response->plugin = $this->product->get_slug() . '/' . $this->product->get_file();
744 }
745 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
746 $_transient_data->response[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
747 } else {
748 $_transient_data->no_update[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
749 }
750 }
751
752 return $_transient_data;
753 }
754
755 /**
756 * Calls the API and, if successfull, returns the object delivered by the API.
757 *
758 * @param string $_action The requested action.
759 * @param array $_data Parameters for the API action.
760 *
761 * @return false||object
762 */
763 private function api_request( $_action = '', $_data = '' ) {
764 $update_data = $this->get_version_data();
765 if ( empty( $update_data ) ) {
766 return false;
767 }
768 if ( $update_data && isset( $update_data->sections ) ) {
769 $update_data->sections = maybe_unserialize( $update_data->sections );
770 }
771
772 return $update_data;
773 }
774
775 /**
776 * Updates information on the "View version x.x details" page with custom data.
777 *
778 * @param mixed $_data Plugin data.
779 * @param string $_action Action to send.
780 * @param object $_args Arguments to use.
781 *
782 * @return object $_data
783 */
784 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
785 if ( ( 'plugin_information' !== $_action ) || ! isset( $_args->slug ) || ( $_args->slug !== $this->product->get_slug() ) ) {
786 return $_data;
787 }
788 $api_response = $this->api_request();
789 if ( false !== $api_response ) {
790 $_data = $api_response;
791 }
792
793 return $_data;
794 }
795
796 /**
797 * Disable SSL verification in order to prevent download update failures.
798 *
799 * @param array $args Http args.
800 * @param string $url Url to check.
801 *
802 * @return array $array
803 */
804 public function http_request_args( $args, $url ) {
805 // If it is an https request and we are performing a package download, disable ssl verification.
806 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
807 $args['sslverify'] = false;
808 }
809
810 return $args;
811 }
812
813 /**
814 * Check if we should load the module for this product.
815 *
816 * @param Product $product Product data.
817 *
818 * @return bool Should we load the module?
819 */
820 public function can_load( $product ) {
821
822 if ( $product->is_wordpress_available() ) {
823 return false;
824 }
825
826 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
827
828 }
829
830 /**
831 * Load module logic.
832 *
833 * @param Product $product Product to load the module for.
834 *
835 * @return Licenser Module object.
836 */
837 public function load( $product ) {
838 $this->product = $product;
839
840 $this->product_key = $this->product->get_key() . '-update-response';
841
842 $this->license_key = $this->product->get_license();
843 if ( $this->product->requires_license() ) {
844 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
845 $this->register_license_hooks();
846 }
847
848 $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
849
850 if ( false !== $namespace ) {
851 $this->namespace = $namespace;
852 add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
853 add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
854 add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
855 add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
856 if ( defined( 'WP_CLI' ) && WP_CLI ) {
857 \WP_CLI::add_command( $namespace . ' activate', [ $this, 'cli_activate' ] );
858 \WP_CLI::add_command( $namespace . ' deactivate', [ $this, 'cli_deactivate' ] );
859 \WP_CLI::add_command( $namespace . ' is-active', [ $this, 'cli_is_active' ] );
860 }
861 }
862
863 add_action( 'admin_head', [ $this, 'auto_activate' ] );
864 if ( $this->product->is_plugin() ) {
865 add_filter(
866 'pre_set_site_transient_update_plugins',
867 [
868 $this,
869 'pre_set_site_transient_update_plugins_filter',
870 ]
871 );
872 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
873 add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
874
875 return $this;
876 }
877 if ( $this->product->is_theme() ) {
878 add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
879 add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
880 add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
881 add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
882 add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
883 add_filter( 'http_request_args', array( $this, 'disable_wporg_update' ), 5, 2 ); //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
884
885 return $this;
886
887 }
888
889 return $this;
890 }
891
892 /**
893 * Register license fields for the products.
894 */
895 public function register_license_hooks() {
896 add_action( 'admin_init', array( $this, 'register_settings' ) );
897 add_action( 'admin_init', array( $this, 'process_license' ) );
898 add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
899 add_action( 'admin_notices', array( $this, 'show_notice' ) );
900 add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
901 }
902
903 /**
904 * Check license on filesystem.
905 *
906 * @return mixed License key.
907 */
908 public function get_file_license() {
909
910 $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
911
912 global $wp_filesystem;
913 if ( ! is_file( $license_file ) ) {
914 return false;
915 }
916
917 require_once ABSPATH . '/wp-admin/includes/file.php';
918 \WP_Filesystem();
919 $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
920 if ( ! is_object( $content ) ) {
921 return false;
922 }
923 if ( ! isset( $content->key ) ) {
924 return false;
925 }
926 return $content->key;
927 }
928 /**
929 * Run license activation on plugin activate.
930 */
931 public function auto_activate() {
932 $status = $this->get_license_status();
933 if ( 'not_active' !== $status ) {
934 return false;
935 }
936
937 if ( ! empty( $this->namespace ) ) {
938 $license_key = apply_filters( 'product_' . $this->namespace . '_license_key_constant', '' );
939 }
940
941 if ( empty( $license_key ) ) {
942 $license_key = $this->get_file_license();
943 }
944 if ( empty( $license_key ) ) {
945 return;
946 }
947
948
949 $this->license_local = $license_key;
950 $lock_key = $this->product->get_key() . '_autoactivated';
951
952 if ( 'yes' === get_option( $lock_key, '' ) ) {
953 return;
954 }
955 if ( 'yes' === get_transient( $lock_key ) ) {
956 return;
957 }
958 $response = $this->do_license_process( $license_key, 'activate' );
959
960 set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
961
962 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
963 return;
964 }
965
966 if ( true === $response ) {
967 add_action( 'admin_notices', [ $this, 'autoactivate_notice' ] );
968 }
969 }
970
971 /**
972 * Show auto-activate notice.
973 */
974 public function autoactivate_notice() {
975 ?>
976 <div class="notice notice-success is-dismissible">
977 <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>
978 </div>
979 <?php
980 }
981
982 /**
983 * Activate product license on this site.
984 *
985 * ## OPTIONS
986 *
987 * @param array $args Command args.
988 *
989 * [<license-key>]
990 * : Product license key.
991 */
992 public function cli_activate( $args ) {
993 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
994 $response = $this->do_license_process( $license_key, 'activate' );
995 if ( true !== $response ) {
996 \WP_CLI::error( $response->get_error_message() );
997
998 return;
999 }
1000
1001 \WP_CLI::success( 'Product successfully activated.' );
1002 }
1003
1004 /**
1005 * Deactivate product license on this site.
1006 *
1007 * @param array $args Command args.
1008 *
1009 * ## OPTIONS
1010 *
1011 * [<license-key>]
1012 * : Product license key.
1013 */
1014 public function cli_deactivate( $args ) {
1015 $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1016 $response = $this->do_license_process( $license_key, 'deactivate' );
1017 if ( true !== $response ) {
1018 \WP_CLI::error( $response->get_error_message() );
1019
1020 return;
1021 }
1022
1023 \WP_CLI::success( 'Product successfully deactivated.' );
1024 }
1025
1026 /**
1027 * Checks if product has license activated.
1028 *
1029 * @param array $args Command args.
1030 *
1031 * @subcommand is-active
1032 */
1033 public function cli_is_active( $args ) {
1034
1035 $status = $this->get_license_status();
1036 if ( 'valid' === $status ) {
1037 \WP_CLI::halt( 0 );
1038
1039 return;
1040 }
1041
1042 \WP_CLI::halt( 1 );
1043 }
1044 }
1045