PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 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 All 135 releases
← All changes | vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php +441 -161 3.0.1 → trunk View file →
@@ -23,16 +23,33 @@
23 23 /**
24 24 * Licenser module for ThemeIsle SDK.
25 25 */
26 26 class Licenser extends Abstract_Module {
27 -
28 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 + /**
29 40 * Number of max failed checks before showing the license message.
30 41 *
31 42 * @var int $max_failed Maximum failed checks allowed before show the notice
32 43 */
33 - private static $max_failed = 5;
44 + private static $max_failed = 1;
34 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 + /**
35 52 * License key string.
36 53 *
37 54 * @var string $license_key The license key string
38 55 */
@@ -61,8 +78,14 @@
61 78 *
62 79 * @var null Local license object.
63 80 */
64 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;
65 88
66 89 /**
67 90 * Disable wporg updates for premium products.
68 91 *
@@ -70,9 +93,9 @@
70 93 * @param string $url The api url.
71 94 *
72 95 * @return mixed List of themes to check for update.
73 96 */
74 - function disable_wporg_update( $r, $url ) {
97 + public function disable_wporg_update( $r, $url ) {
75 98
76 99 if ( 0 !== strpos( $url, 'https://api.wordpress.org/themes/update-check/' ) ) {
77 100 return $r;
78 101 }
@@ -82,9 +105,9 @@
82 105
83 106 unset( $themes->themes->{$this->product->get_slug()} );
84 107
85 108 // Encode the updated JSON response.
86 - $r['body']['themes'] = json_encode( $themes );
109 + $r['body']['themes'] = wp_json_encode( $themes );
87 110
88 111 return $r;
89 112 }
90 113
@@ -114,13 +137,13 @@
114 137 public function license_view() {
115 138 $status = $this->get_license_status();
116 139 $value = $this->license_key;
117 140
118 - $activate_string = apply_filters( $this->product->get_key() . '_lc_activate_string', 'Activate' );
119 - $deactivate_string = apply_filters( $this->product->get_key() . '_lc_deactivate_string', 'Deactivate' );
120 - $valid_string = apply_filters( $this->product->get_key() . '_lc_valid_string', 'Valid' );
121 - $invalid_string = apply_filters( $this->product->get_key() . '_lc_invalid_string', 'Invalid' );
122 - $license_message = apply_filters( $this->product->get_key() . '_lc_license_message', 'Enter your license from %s purchase history in order to get %s updates' );
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'] );
123 146 $error_message = $this->get_error();
124 147 ?>
125 148 <style type="text/css">
126 149 input.themeisle-sdk-text-input-valid {
@@ -171,20 +194,20 @@
171 194 </style>
172 195 <?php
173 196 echo sprintf(
174 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',
175 - ( ( 'valid' === $status ) ? sprintf( '<input type="hidden" value="%s" name="%s_license" />', $value, $this->product->get_key() ) : '' ),
198 + ( ( 'valid' === $status ) ? sprintf( '<input type="hidden" value="%s" name="%s_license" />', esc_attr( $value ), esc_attr( $this->product->get_key() ) ) : '' ),
176 199 ( ( 'valid' === $status ) ? 'themeisle-sdk-text-input-valid' : '' ),
177 - $this->product->get_key(),
178 - ( ( 'valid' === $status ) ? $this->product->get_key() . '_mask' : $this->product->get_key() ),
179 - ( ( 'valid' === $status ) ? ( str_repeat( '*', 30 ) . substr( $value, - 5 ) ) : $value ),
180 - ( 'valid' === $status ? 'themeisle-sdk-license-deactivate-cta' : 'themeisle-sdk-license-activate-cta' ),
181 - ( 'valid' === $status ? $valid_string : $invalid_string ),
182 - $this->product->get_key(),
183 - ( 'valid' === $status ? $deactivate_string : $activate_string ),
184 - sprintf( $license_message, '<a href="' . $this->get_api_url() . '">' . $this->get_distributor_name() . '</a> ', $this->product->get_type() ),
185 - empty( $error_message ) ? '' : sprintf( '<p style="color:#dd3d36">%s</p>', $error_message )
186 - );
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
187 210
188 211 }
189 212
190 213 /**
@@ -209,22 +232,104 @@
209 232 return ( 'valid' === $status && isset( $license_data->is_expired ) && 'yes' === $license_data->is_expired ) ? 'active_expired' : $status;
210 233 }
211 234
212 235 /**
213 - * License price id.
236 + * Check status.
214 237 *
215 - * @return int License plan.
238 + * @param string $product_file Product basefile.
239 + *
240 + * @return string Status license.
216 241 */
217 - public function get_plan() {
218 - $license_data = get_option( $this->product->get_key() . '_license_data', '' );
219 - if ( ! isset( $license_data->price_id ) ) {
220 - return -1;
242 + public static function status( $product_file ) {
243 + $product = Product::get( $product_file );
244 + if ( ! $product->requires_license() ) {
245 + return self::STATUS_VALID;
221 246 }
247 + $license_data = self::get_license_data( $product->get_key() );
222 248
223 - return (int) $license_data->price_id;
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;
224 252 }
225 253
226 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 + /**
227 332 * Get remote api url.
228 333 *
229 334 * @return string Remote api url.
230 335 */
@@ -242,9 +347,9 @@
242 347 * @return string Remote api url.
243 348 */
244 349 public function get_distributor_name() {
245 350 if ( $this->is_from_partner( $this->product ) ) {
246 - return 'ThemeIsle';
351 + return 'Themeisle';
247 352 }
248 353
249 354 return $this->product->get_store_name();
250 355 }
@@ -249,13 +354,22 @@
249 354 return $this->product->get_store_name();
250 355 }
251 356
252 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 + /**
253 367 * Show the admin notice regarding the license status.
254 368 *
255 369 * @return bool Should we show the notice ?
256 370 */
257 - function show_notice() {
371 + public function show_notice() {
258 372 if ( ! is_admin() ) {
259 373 return false;
260 374 }
261 375
@@ -263,11 +377,11 @@
263 377 return false;
264 378 }
265 379
266 380 $status = $this->get_license_status( true );
267 - $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.' );
268 - $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>. ' );
269 - $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.' );
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_heading_string', Loader::$labels['licenser']['expired'] );
270 384 // No activations left for this license.
271 385 if ( 'valid' != $status && $this->check_activation() ) {
272 386 ?>
273 387 <div class="error">
@@ -273,12 +387,12 @@
273 387 <div class="error">
274 388 <p><strong>
275 389 <?php
276 390 echo sprintf(
277 - $no_activations_string,
278 - $this->product->get_name(),
279 - $this->product->get_name(),
280 - '<a href="' . $this->get_api_url() . '" target="_blank">' . $this->get_distributor_name() . '</a>'
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>'
281 395 );
282 396 ?>
283 397 </strong>
284 398 </p>
@@ -288,14 +402,74 @@
288 402 }
289 403
290 404 // Invalid license key.
291 405 if ( 'active_expired' === $status ) {
406 + // Check if the notice was dismissed.
407 + $dismiss_option_key = $this->product->get_key() . '_expired_notice_dismissed';
408 + if ( get_option( $dismiss_option_key, false ) ) {
409 + return false;
410 + }
411 +
412 + $license_data = get_option( $this->product->get_key() . '_license_data', '' );
413 + $expiration_date = '';
414 + if ( is_object( $license_data ) && isset( $license_data->expires ) ) {
415 + $timestamp = strtotime( (string) $license_data->expires );
416 + if ( false !== $timestamp ) {
417 + $expiration_date = gmdate( 'F j, Y', $timestamp );
418 + }
419 + }
420 +
421 + $discount_config = apply_filters( $this->product->get_key() . '_lc_renew_discount', false );
422 +
423 + if ( is_array( $discount_config ) && isset( $discount_config['url'] ) && isset( $discount_config['renew_button'] ) ) {
424 + $renew_url = $discount_config['url'];
425 + $renew_button = $discount_config['renew_button'];
426 + } else {
427 + $renew_url = apply_filters( $this->product->get_key() . '_lc_renew_url', $this->renew_url() );
428 + $renew_button = apply_filters( $this->product->get_key() . '_lc_renew_button_string', Loader::$labels['licenser']['renew_license'] );
429 + }
430 +
431 + $learn_more_url = apply_filters( $this->product->get_key() . '_lc_learn_more_url', $this->get_api_url() );
432 + $learn_more_button = apply_filters( $this->product->get_key() . '_lc_learn_more_button_string', Loader::$labels['licenser']['learn_more'] );
433 + $notice_message = apply_filters( $this->product->get_key() . '_lc_expired_notice_message', Loader::$labels['licenser']['expired_notice'] );
434 +
435 + $expired_date_string = apply_filters( $this->product->get_key() . '_lc_expired_date_string', sprintf( Loader::$labels['licenser']['expired_date'], esc_html( $expiration_date ) ) );
436 + $heading = apply_filters( $this->product->get_key() . '_lc_expired_heading_string', sprintf( Loader::$labels['licenser']['expired'], $this->product->get_name() ) );
437 + $notice_id = $this->product->get_key() . '_expired_notice';
292 438 ?>
293 - <div class="error">
294 - <p>
295 - <strong><?php echo sprintf( $expired_license_string, $this->product->get_name() . ' ' . $this->product->get_type(), $this->get_api_url() . '?license=' . $this->license_key ); ?> </strong>
439 + <div class="notice notice-warning notice-alt is-dismissible themeisle-sdk-license-notice" id="<?php echo esc_attr( $notice_id ); ?>" data-notice-id="<?php echo esc_attr( $notice_id ); ?>" style="position: relative; border-left: 4px solid #d63638; padding: 12px; background-color: #fff;">
440 + <p style="margin: 0.5em 0; font-size: 13px;">
441 + <strong><?php echo wp_kses_post( $heading ); ?></strong>
442 + · <?php echo esc_html( $expired_date_string ); ?>
296 443 </p>
444 + <p style="margin: 0.5em 0 1em 0; font-size: 13px;">
445 + <?php echo esc_html( $notice_message ); ?>
446 + </p>
447 + <p style="margin: 0.5em 0;">
448 + <a href="<?php echo esc_url( $renew_url ); ?>" class="button button-primary" target="_blank" rel="noopener noreferrer">
449 + <?php echo esc_html( $renew_button ); ?>
450 + </a>
451 + <a href="<?php echo esc_url( $learn_more_url ); ?>" class="button" target="_blank" rel="noopener noreferrer" style="border: none; background-color: transparent;">
452 + <?php echo esc_html( $learn_more_button ); ?>
453 + </a>
454 + </p>
297 455 </div>
456 + <script type="text/javascript">
457 + jQuery(document).ready(function($) {
458 + $('#<?php echo esc_js( $notice_id ); ?>').on('click', '.notice-dismiss', function(e) {
459 + const noticeId = '<?php echo esc_js( $notice_id ); ?>';
460 + $.ajax({
461 + url: ajaxurl,
462 + type: 'POST',
463 + data: {
464 + action: 'themeisle_sdk_dismiss_license_notice',
465 + notice_id: noticeId,
466 + nonce: '<?php echo esc_js( wp_create_nonce( 'themeisle_sdk_dismiss_license_notice' ) ); ?>'
467 + }
468 + });
469 + });
470 + });
471 + </script>
298 472 <?php
299 473
300 474 return false;
301 475 }
@@ -303,9 +477,9 @@
303 477 if ( 'valid' != $status ) {
304 478 ?>
305 479 <div class="error">
306 480 <p>
307 - <strong><?php echo sprintf( $no_valid_string, $this->product->get_name() . ' ' . $this->product->get_type(), $this->get_api_url(), admin_url( 'options-general.php' ) . '#' . $this->product->get_key() . '_license' ); ?> </strong>
481 + <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>
308 482 </p>
309 483 </div>
310 484 <?php
311 485
@@ -334,9 +508,9 @@
334 508 * Check if the license is about to expire in the next month.
335 509 *
336 510 * @return bool
337 511 */
338 - function check_expiration() {
512 + public function check_expiration() {
339 513 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
340 514 if ( '' === $license_data ) {
341 515 return false;
342 516 }
@@ -354,9 +528,9 @@
354 528 * Return the renew url from the store used.
355 529 *
356 530 * @return string The renew url.
357 531 */
358 - function renew_url() {
532 + public function renew_url() {
359 533 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
360 534 if ( '' === $license_data ) {
361 535 return $this->get_api_url();
362 536 }
@@ -363,9 +537,9 @@
363 537 if ( ! isset( $license_data->download_id ) || ! isset( $license_data->key ) ) {
364 538 return $this->get_api_url();
365 539 }
366 540
367 - return $this->get_api_url() . '/checkout/?edd_license_key=' . $license_data->key . '&download_id=' . $license_data->download_id;
541 + return trim( $this->get_api_url(), '/' ) . '/checkout/?edd_license_key=' . $license_data->key . '&download_id=' . $license_data->download_id;
368 542 }
369 543
370 544 /**
371 545 * Run the license check call.
@@ -370,9 +544,9 @@
370 544 /**
371 545 * Run the license check call.
372 546 */
373 547 public function product_valid() {
374 - if ( false !== ( $license = get_transient( $this->product->get_key() . '_license_data' ) ) ) {
548 + if ( false !== ( $license = get_transient( $this->product->get_key() . '_license_data' ) ) ) { //phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
375 549 return;
376 550 }
377 551 $license = $this->check_license();
378 552 set_transient( $this->product->get_key() . '_license_data', $license, 12 * HOUR_IN_SECONDS );
@@ -397,22 +571,17 @@
397 571 $response = $this->do_license_process( $license, 'check' );
398 572
399 573 if ( is_wp_error( $response ) ) {
400 574 $license_data = new \stdClass();
401 - $license_data->license = 'valid';
575 + $license_data->license = 'invalid';
402 576 } else {
403 577 $license_data = $response;
404 578 }
405 579
406 580 $license_old = get_option( $this->product->get_key() . '_license_data', '' );
407 -
408 - if ( 'valid' === $license_old->license && ( $license_data->license !== $license_old->license ) ) {
581 + if ( 'valid' === $license_old->license && ( $license_data->license !== $license_old->license ) && $this->failed_checks <= self::$max_failed ) {
409 582 $this->increment_failed_checks();
410 - } else {
411 - $this->reset_failed_checks();
412 - }
413 583
414 - if ( $this->failed_checks <= self::$max_failed ) {
415 584 return $license_old;
416 585 }
417 586
418 587 if ( ! isset( $license_data->key ) ) {
@@ -417,8 +586,9 @@
417 586
418 587 if ( ! isset( $license_data->key ) ) {
419 588 $license_data->key = isset( $license_old->key ) ? $license_old->key : '';
420 589 }
590 + $this->reset_failed_checks();
421 591
422 592 return $license_data;
423 593
424 594 }
@@ -423,44 +593,8 @@
423 593
424 594 }
425 595
426 596 /**
427 - * Increment the failed checks.
428 - */
429 - private function increment_failed_checks() {
430 - $this->failed_checks ++;
431 - update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
432 - }
433 -
434 - /**
435 - * Reset the failed checks
436 - */
437 - private function reset_failed_checks() {
438 - $this->failed_checks = 1;
439 - update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
440 - }
441 -
442 - /**
443 - * Set license validation error message.
444 - *
445 - * @param string $message Error message.
446 - */
447 - public function set_error( $message = '' ) {
448 - set_transient( $this->product->get_key() . 'act_err', $message, MINUTE_IN_SECONDS );
449 -
450 - return;
451 - }
452 -
453 - /**
454 - * Return the last error message.
455 - *
456 - * @return mixed Error message.
457 - */
458 - public function get_error() {
459 - return get_transient( $this->product->get_key() . 'act_err' );
460 - }
461 -
462 - /**
463 597 * Do license activation/deactivation.
464 598 *
465 599 * @param string $license License key.
466 600 * @param string $action What do to.
@@ -468,17 +602,17 @@
468 602 * @return bool|\WP_Error
469 603 */
470 604 public function do_license_process( $license, $action = 'toggle' ) {
471 605 if ( strlen( $license ) < 10 ) {
472 - return new \WP_Error( 'themeisle-license-invalid-format', 'Invalid license.' );
606 + return new \WP_Error( 'themeisle-license-invalid-format', Loader::$labels['licenser']['invalid_msg'] );
473 607 }
474 608 $status = $this->get_license_status();
475 609
476 610 if ( 'valid' === $status && 'activate' === $action ) {
477 - return new \WP_Error( 'themeisle-license-already-active', 'License is already active.' );
611 + return new \WP_Error( 'themeisle-license-already-active', Loader::$labels['licenser']['already_active'] );
478 612 }
479 613 if ( 'valid' !== $status && 'deactivate' === $action ) {
480 - return new \WP_Error( 'themeisle-license-already-deactivate', 'License not active.' );
614 + return new \WP_Error( 'themeisle-license-already-deactivate', Loader::$labels['licenser']['not_active'] );
481 615 }
482 616
483 617 if ( 'toggle' === $action ) {
484 618 $action = ( 'valid' !== $status ? ( 'activate' ) : ( 'deactivate' ) );
@@ -485,9 +619,9 @@
485 619 }
486 620
487 621 // Call the custom API.
488 622 if ( 'check' === $action ) {
489 - $response = wp_remote_get( sprintf( '%slicense/check/%s/%s/%s/%s', Product::API_URL, rawurlencode( $this->product->get_name() ), $license, rawurlencode( home_url() ), Loader::get_cache_token() ) );
623 + $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() ) );
490 624 } else {
491 625 $response = wp_remote_post(
492 626 sprintf( '%slicense/%s/%s/%s', Product::API_URL, $action, rawurlencode( $this->product->get_name() ), $license ),
493 627 array(
@@ -504,15 +638,15 @@
504 638 }
505 639
506 640 // make sure the response came back okay.
507 641 if ( is_wp_error( $response ) ) {
508 - 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() ) );
642 + return new \WP_Error( 'themeisle-license-500', sprintf( Loader::$labels['licenser']['error_notice'], $response->get_error_message() ) );
509 643 }
510 644
511 645 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
512 646
513 647 if ( ! is_object( $license_data ) ) {
514 - return new \WP_Error( 'themeisle-license-404', 'ERROR: Failed to validate license. Please try again in one minute.' );
648 + return new \WP_Error( 'themeisle-license-404', Loader::$labels['licenser']['error_notice2'] );
515 649 }
516 650 if ( 'check' === $action ) {
517 651 return $license_data;
518 652 }
@@ -543,23 +677,54 @@
543 677 }
544 678 update_option( $this->product->get_key() . '_license_data', $license_data );
545 679 set_transient( $this->product->get_key() . '_license_data', $license_data, 12 * HOUR_IN_SECONDS );
546 680 if ( 'activate' === $action && 'valid' !== $license_data->license ) {
547 - return new \WP_Error( 'themeisle-license-invalid', 'ERROR: Invalid license provided.' );
681 + return new \WP_Error( 'themeisle-license-invalid', Loader::$labels['licenser']['error_invalid'] );
548 682 }
549 683
684 + // Remove the versions transient upon activation so that newer version for rollback can be acquired.
685 + $versions_cache = $this->product->get_cache_key();
686 + delete_transient( $versions_cache );
687 +
550 688 return true;
551 689 }
552 690
553 691 /**
692 + * Reset the failed checks
693 + */
694 + private function reset_failed_checks() {
695 + $this->failed_checks = 1;
696 + update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
697 + }
698 +
699 + /**
700 + * Increment the failed checks.
701 + */
702 + private function increment_failed_checks() {
703 + $this->failed_checks ++;
704 + update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
705 + }
706 +
707 + /**
554 708 * Activate the license remotely.
555 709 */
556 - function process_license() {
710 + public function process_license() {
557 711 // listen for our activate button to be clicked.
558 712 if ( ! isset( $_POST[ $this->product->get_key() . '_btn_trigger' ] ) ) {
559 713 return;
560 714 }
561 - $license = $_POST[ $this->product->get_key() . '_license' ];
715 + if ( ! isset( $_POST[ $this->product->get_key() . 'nonce_field' ] )
716 + || ! wp_verify_nonce( $_POST[ $this->product->get_key() . 'nonce_field' ], $this->product->get_key() . 'nonce' ) //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
717 + ) {
718 + return;
719 + }
720 + if ( ! current_user_can( 'manage_options' ) ) {
721 + return;
722 + }
723 + $license = isset( $_POST[ $this->product->get_key() . '_license' ] )
724 + ? sanitize_text_field( $_POST[ $this->product->get_key() . '_license' ] )
725 + : '';
726 +
562 727 $response = $this->do_license_process( $license, 'toggle' );
563 728 if ( is_wp_error( $response ) ) {
564 729 $this->set_error( $response->get_error_message() );
565 730
@@ -567,16 +732,24 @@
567 732 }
568 733 if ( true === $response ) {
569 734 $this->set_error( '' );
570 735 }
736 + }
571 737
572 - return;
738 + /**
739 + * Set license validation error message.
740 + *
741 + * @param string $message Error message.
742 + */
743 + public function set_error( $message = '' ) {
744 + set_transient( $this->product->get_key() . 'act_err', $message, MINUTE_IN_SECONDS );
745 +
573 746 }
574 747
575 748 /**
576 749 * Load the Themes screen.
577 750 */
578 - function load_themes_screen() {
751 + public function load_themes_screen() {
579 752 add_thickbox();
580 753 add_action( 'admin_notices', array( &$this, 'update_nag' ) );
581 754 }
582 755
@@ -582,9 +755,9 @@
582 755
583 756 /**
584 757 * Alter the nag for themes update.
585 758 */
586 - function update_nag() {
759 + public function update_nag() {
587 760 $theme = wp_get_theme( $this->product->get_slug() );
588 761 $api_response = get_transient( $this->product_key );
589 762 if ( false === $api_response || ! isset( $api_response->new_version ) ) {
590 763 return;
@@ -589,24 +762,22 @@
589 762 if ( false === $api_response || ! isset( $api_response->new_version ) ) {
590 763 return;
591 764 }
592 765 $update_url = wp_nonce_url( 'update.php?action=upgrade-theme&amp;theme=' . urlencode( $this->product->get_slug() ), 'upgrade-theme_' . $this->product->get_slug() );
593 - $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.' );
766 + $update_message = apply_filters( 'themeisle_sdk_license_update_message', Loader::$labels['licenser']['update_license'] );
594 767 $update_onclick = ' onclick="if ( confirm(\'' . esc_js( $update_message ) . '\') ) {return true;}return false;"';
595 768 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
596 769 echo '<div id="update-nag">';
597 770 printf(
598 - '<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>.',
599 - $theme->get( 'Name' ),
600 - $api_response->new_version,
601 - sprintf( '%s&TB_iframe=true&amp;width=1024&amp;height=800', $this->product->get_changelog() ),
602 - $theme->get( 'Name' ),
603 - $update_url,
604 - $update_onclick
771 + esc_html( Loader::$labels['licenser']['notice_update'] ),
772 + '<strong>' . esc_attr( $theme->get( 'Name' ) ) . ' ' . esc_attr( $api_response->new_version ) . '</strong>',
773 + '<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' ) ) . '">',
774 + '</a>',
775 + '<a href="' . esc_url( $update_url ) . '" ' . $update_onclick . '>' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Already escaped.
605 776 );
606 777 echo '</div>';
607 - echo '<div id="' . $this->product->get_slug() . '_' . 'changelog" style="display:none;">';
608 - echo wpautop( $api_response->sections['changelog'] );
778 + echo '<div id="' . esc_attr( $this->product->get_slug() ) . '_changelog" style="display:none;">';
779 + echo wp_kses_data( wpautop( $api_response->sections['changelog'] ) );
609 780 echo '</div>';
610 781 }
611 782 }
612 783
@@ -616,14 +787,24 @@
616 787 * @param mixed $value The transient data.
617 788 *
618 789 * @return mixed
619 790 */
620 - function theme_update_transient( $value ) {
791 + public function theme_update_transient( $value ) {
621 792 $update_data = $this->check_for_update();
622 - if ( $update_data ) {
623 - $value->response[ $this->product->get_slug() ] = $update_data;
793 + if ( empty( $value ) ) {
794 + return $value;
624 795 }
625 796
797 + if ( ! isset( $value->response ) ) {
798 + return $value;
799 + }
800 +
801 + if ( ! $update_data ) {
802 + return $value;
803 + }
804 +
805 + $value->response[ $this->product->get_slug() ] = $update_data;
806 +
626 807 return $value;
627 808 }
628 809
629 810 /**
@@ -630,9 +811,9 @@
630 811 * Check for updates
631 812 *
632 813 * @return array|bool Either the update data or false in case of failure.
633 814 */
634 - function check_for_update() {
815 + public function check_for_update() {
635 816 $update_data = get_transient( $this->product_key );
636 817
637 818 if ( false === $update_data ) {
638 819 $failed = false;
@@ -668,9 +849,9 @@
668 849 * @return bool|mixed Update api response.
669 850 */
670 851 private function get_version_data() {
671 852
672 - $response = wp_remote_get(
853 + $response = $this->safe_get(
673 854 sprintf(
674 855 '%slicense/version/%s/%s/%s/%s',
675 856 Product::API_URL,
676 857 rawurlencode( $this->product->get_name() ),
@@ -678,9 +859,9 @@
678 859 $this->product->get_version(),
679 860 rawurlencode( home_url() )
680 861 ),
681 862 array(
682 - 'timeout' => 15,
863 + 'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
683 864 'sslverify' => false,
684 865 )
685 866 );
686 867 if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
@@ -705,10 +886,10 @@
705 886
706 887 /**
707 888 * Delete the update transient
708 889 */
709 - function delete_theme_update_transient() {
710 - delete_transient( $this->product_key );
890 + public function delete_theme_update_transient() {
891 + return delete_transient( $this->product_key );
711 892 }
712 893
713 894 /**
714 895 * Check for Updates at the defined API endpoint and modify the update array.
@@ -722,12 +903,21 @@
722 903 $this->do_check = true;
723 904
724 905 return $_transient_data;
725 906 }
907 + $this->product->setup_from_fileheaders();
726 908 $api_response = $this->api_request();
727 909 if ( false !== $api_response && is_object( $api_response ) && isset( $api_response->new_version ) ) {
910 + $plugin = $this->product->get_slug() . '/' . $this->product->get_file();
911 + if ( ! isset( $api_response->plugin ) ) {
912 + $api_response->plugin = $plugin;
913 + }
728 914 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
729 - $_transient_data->response[ $this->product->get_slug() . '/' . $this->product->get_file() ] = $api_response;
915 + $_transient_data->response[ $plugin ] = $api_response;
916 + unset( $_transient_data->no_update[ $plugin ] );
917 + } else {
918 + $_transient_data->no_update[ $plugin ] = $api_response;
919 + unset( $_transient_data->response[ $plugin ] );
730 920 }
731 921 }
732 922
733 923 return $_transient_data;
@@ -781,9 +971,9 @@
781 971 * @param string $url Url to check.
782 972 *
783 973 * @return array $array
784 974 */
785 - function http_request_args( $args, $url ) {
975 + public function http_request_args( $args, $url ) {
786 976 // If it is an https request and we are performing a package download, disable ssl verification.
787 977 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
788 978 $args['sslverify'] = false;
789 979 }
@@ -799,9 +989,9 @@
799 989 * @return bool Should we load the module?
800 990 */
801 991 public function can_load( $product ) {
802 992
803 - if ( $product->is_wordpress_available() ) {
993 + if ( ! $product->requires_license() ) {
804 994 return false;
805 995 }
806 996
807 997 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
@@ -824,12 +1014,19 @@
824 1014 if ( $this->product->requires_license() ) {
825 1015 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
826 1016 $this->register_license_hooks();
827 1017 }
828 -
1018 + if ( ! self::$globals_loaded ) {
1019 + add_filter( 'themeisle_sdk_license/status', [ __CLASS__, 'status' ], 999, 1 );
1020 + add_filter( 'themeisle_sdk_license/is-valid', [ __CLASS__, 'is_valid' ], 999, 1 );
1021 + add_filter( 'themeisle_sdk_license/plan', [ __CLASS__, 'plan' ], 999, 1 );
1022 + add_filter( 'themeisle_sdk_license/key', [ __CLASS__, 'key' ], 999, 1 );
1023 + $globals_loaded = true;
1024 + }
829 1025 $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
830 1026
831 1027 if ( false !== $namespace ) {
1028 + $this->namespace = $namespace;
832 1029 add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
833 1030 add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
834 1031 add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
835 1032 add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
@@ -841,27 +1038,61 @@
841 1038 }
842 1039
843 1040 add_action( 'admin_head', [ $this, 'auto_activate' ] );
844 1041 if ( $this->product->is_plugin() ) {
845 - add_filter(
846 - 'pre_set_site_transient_update_plugins',
847 - [
848 - $this,
849 - 'pre_set_site_transient_update_plugins_filter',
850 - ]
851 - );
852 - add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
853 - add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 );
1042 + if ( ! $product->is_wordpress_available() ) {
1043 + add_filter(
1044 + 'pre_set_site_transient_update_plugins',
1045 + [
1046 + $this,
1047 + 'pre_set_site_transient_update_plugins_filter',
1048 + ]
1049 + );
1050 + add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
1051 + add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
1052 + 'http_request_args',
1053 + array(
1054 + $this,
1055 + 'http_request_args',
1056 + ),
1057 + 10,
1058 + 2
1059 + );
1060 + if ( ! self::is_valid( $product->get_basefile() ) ) {
1061 + add_filter(
1062 + 'plugin_action_links_' . plugin_basename( $product->get_basefile() ),
1063 + function ( $actions ) {
1064 + if ( $this->get_license_status( true ) !== self::STATUS_ACTIVE_EXPIRED ) {
1065 + return $actions;
1066 + }
1067 + $new_actions['deactivate'] = $actions['deactivate'];
1068 + $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>';
854 1069
1070 + return $new_actions;
1071 + }
1072 + );
1073 + }
1074 + }
1075 +
855 1076 return $this;
856 1077 }
857 1078 if ( $this->product->is_theme() ) {
858 - add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
859 - add_filter( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
860 - add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
861 - add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
862 - add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
863 - add_filter( 'http_request_args', array( $this, 'disable_wporg_update' ), 5, 2 );
1079 + if ( ! $product->is_wordpress_available() ) {
1080 + add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
1081 + add_action( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
1082 + add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
1083 + add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
1084 + add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
1085 + add_filter( //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args
1086 + 'http_request_args',
1087 + array(
1088 + $this,
1089 + 'disable_wporg_update',
1090 + ),
1091 + 5,
1092 + 2
1093 + );
1094 + }
864 1095
865 1096 return $this;
866 1097
867 1098 }
@@ -869,44 +1100,104 @@
869 1100 return $this;
870 1101 }
871 1102
872 1103 /**
873 - * Run license activation on plugin activate.
1104 + * Register license fields for the products.
874 1105 */
875 - public function auto_activate() {
876 - if ( ! current_user_can( 'switch_themes' ) ) {
877 - return;
1106 + public function register_license_hooks() {
1107 + add_action( 'admin_init', array( $this, 'register_settings' ) );
1108 + add_action( 'admin_init', array( $this, 'process_license' ) );
1109 + add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
1110 + add_action( 'admin_notices', array( $this, 'show_notice' ) );
1111 + add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
1112 + add_action( 'wp_ajax_themeisle_sdk_dismiss_license_notice', array( $this, 'dismiss_license_notice' ) );
1113 + }
1114 +
1115 + /**
1116 + * Handle AJAX request to dismiss the license notice.
1117 + */
1118 + public function dismiss_license_notice() {
1119 + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'themeisle_sdk_dismiss_license_notice' ) ) {
1120 + wp_send_json_error( 'Invalid nonce' );
878 1121 }
879 - $status = $this->get_license_status();
880 - if ( 'not_active' !== $status ) {
881 - return;
1122 +
1123 + if ( ! current_user_can( 'manage_options' ) ) {
1124 + wp_send_json_error( 'Insufficient permissions' );
882 1125 }
883 1126
1127 + $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( $_POST['notice_id'] ) : '';
1128 +
1129 + if ( empty( $notice_id ) ) {
1130 + wp_send_json_error( 'Missing notice ID' );
1131 + }
1132 +
1133 + // Save the dismissal option.
1134 + $dismiss_option_key = $notice_id . '_dismissed';
1135 + update_option( $dismiss_option_key, true );
1136 +
1137 + wp_send_json_success();
1138 + }
1139 +
1140 + /**
1141 + * Check license on filesystem.
1142 + *
1143 + * @return mixed License key.
1144 + */
1145 + public function get_file_license() {
1146 +
884 1147 $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
885 1148
886 1149 global $wp_filesystem;
887 1150 if ( ! is_file( $license_file ) ) {
888 - return;
1151 + return false;
889 1152 }
890 1153
891 - require_once( ABSPATH . '/wp-admin/includes/file.php' );
1154 + require_once ABSPATH . '/wp-admin/includes/file.php';
892 1155 \WP_Filesystem();
893 1156 $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
894 1157 if ( ! is_object( $content ) ) {
895 - return;
1158 + return false;
896 1159 }
897 1160 if ( ! isset( $content->key ) ) {
1161 + return false;
1162 + }
1163 +
1164 + return $content->key;
1165 + }
1166 +
1167 + /**
1168 + * Run license activation on plugin activate.
1169 + */
1170 + public function auto_activate() {
1171 + $status = $this->get_license_status();
1172 + if ( 'not_active' !== $status ) {
1173 + return false;
1174 + }
1175 +
1176 + if ( ! empty( $this->namespace ) ) {
1177 + $license_key = apply_filters( 'product_' . $this->namespace . '_license_key_constant', '' );
1178 + }
1179 +
1180 + if ( empty( $license_key ) ) {
1181 + $license_key = $this->get_file_license();
1182 + }
1183 + if ( empty( $license_key ) ) {
898 1184 return;
899 1185 }
900 - $this->license_local = $content;
1186 +
1187 +
1188 + $this->license_local = $license_key;
901 1189 $lock_key = $this->product->get_key() . '_autoactivated';
902 1190
903 1191 if ( 'yes' === get_option( $lock_key, '' ) ) {
904 1192 return;
905 1193 }
906 - $response = $this->do_license_process( $content->key, 'activate' );
1194 + if ( 'yes' === get_transient( $lock_key ) ) {
1195 + return;
1196 + }
1197 + $response = $this->do_license_process( $license_key, 'activate' );
907 1198
908 - update_option( $lock_key, 'yes' );
1199 + set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
909 1200
910 1201 if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
911 1202 return;
912 1203 }
@@ -921,9 +1212,9 @@
921 1212 */
922 1213 public function autoactivate_notice() {
923 1214 ?>
924 1215 <div class="notice notice-success is-dismissible">
925 - <p><?php echo sprintf( '<strong>%s</strong> has been successfully activated using <strong>%s</strong> license !', $this->product->get_name(), str_repeat( '*', 20 ) . substr( $this->license_local->key, - 10 ) ); ?></p>
1216 + <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>
926 1217 </div>
927 1218 <?php
928 1219 }
929 1220
@@ -987,17 +1278,6 @@
987 1278 return;
988 1279 }
989 1280
990 1281 \WP_CLI::halt( 1 );
991 - }
992 -
993 - /**
994 - * Register license fields for the products.
995 - */
996 - public function register_license_hooks() {
997 - add_action( 'admin_init', array( $this, 'register_settings' ) );
998 - add_action( 'admin_init', array( $this, 'process_license' ) );
999 - add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
1000 - add_action( 'admin_notices', array( $this, 'show_notice' ) );
1001 - add_filter( $this->product->get_key() . '_license_status', array( $this, 'get_license_status' ) );
1002 1282 }
1003 1283 }