PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.9
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.9
4.0.9 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 All 150 releases
← All changes | vendor/codeinwp/themeisle-sdk/src/Modules/Licenser.php +742 -178 3.1.3 → 4.0.9 View file →
@@ -12,8 +12,9 @@
12 12 namespace ThemeisleSDK\Modules;
13 13
14 14 // Exit if accessed directly.
15 15 use ThemeisleSDK\Common\Abstract_Module;
16 +use ThemeisleSDK\Loader;
16 17 use ThemeisleSDK\Product;
17 18
18 19 if ( ! defined( 'ABSPATH' ) ) {
19 20 exit;
@@ -22,16 +23,33 @@
22 23 /**
23 24 * Licenser module for ThemeIsle SDK.
24 25 */
25 26 class Licenser extends Abstract_Module {
26 -
27 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 + /**
28 40 * Number of max failed checks before showing the license message.
29 41 *
30 42 * @var int $max_failed Maximum failed checks allowed before show the notice
31 43 */
32 - private static $max_failed = 5;
44 + private static $max_failed = 1;
33 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 + /**
34 52 * License key string.
35 53 *
36 54 * @var string $license_key The license key string
37 55 */
@@ -55,8 +73,21 @@
55 73 */
56 74 private $product_key;
57 75
58 76 /**
77 + * Holds local license object.
78 + *
79 + * @var null Local license object.
80 + */
81 + private $license_local = null;
82 + /**
83 + * Product namespace, used for fixed name filters/cli commands.
84 + *
85 + * @var string $namespace Product namespace.
86 + */
87 + private $namespace = null;
88 +
89 + /**
59 90 * Disable wporg updates for premium products.
60 91 *
61 92 * @param string $r Update payload.
62 93 * @param string $url The api url.
@@ -62,9 +93,9 @@
62 93 * @param string $url The api url.
63 94 *
64 95 * @return mixed List of themes to check for update.
65 96 */
66 - function disable_wporg_update( $r, $url ) {
97 + public function disable_wporg_update( $r, $url ) {
67 98
68 99 if ( 0 !== strpos( $url, 'https://api.wordpress.org/themes/update-check/' ) ) {
69 100 return $r;
70 101 }
@@ -74,9 +105,9 @@
74 105
75 106 unset( $themes->themes->{$this->product->get_slug()} );
76 107
77 108 // Encode the updated JSON response.
78 - $r['body']['themes'] = json_encode( $themes );
109 + $r['body']['themes'] = wp_json_encode( $themes );
79 110
80 111 return $r;
81 112 }
82 113
@@ -88,8 +119,11 @@
88 119 public function register_settings() {
89 120 if ( ! is_admin() ) {
90 121 return false;
91 122 }
123 + if ( apply_filters( $this->product->get_key() . '_hide_license_field', false ) ) {
124 + return;
125 + }
92 126 add_settings_field(
93 127 $this->product->get_key() . '_license',
94 128 $this->product->get_name() . ' license',
95 129 array( $this, 'license_view' ),
@@ -103,24 +137,88 @@
103 137 public function license_view() {
104 138 $status = $this->get_license_status();
105 139 $value = $this->license_key;
106 140
107 - $activate_string = apply_filters( $this->product->get_key() . '_lc_activate_string', 'Activate' );
108 - $deactivate_string = apply_filters( $this->product->get_key() . '_lc_deactivate_string', 'Deactivate' );
109 - $valid_string = apply_filters( $this->product->get_key() . '_lc_valid_string', 'Valid' );
110 - $invalid_string = apply_filters( $this->product->get_key() . '_lc_invalid_string', 'Invalid' );
111 - $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'] );
146 + $error_message = $this->get_error();
147 + ?>
148 + <style type="text/css">
149 + input.themeisle-sdk-text-input-valid {
150 + border: 1px solid #7ad03a;
151 + }
112 152
113 - echo '<p ><input ' . ( ( 'valid' === $status ) ? ( 'style="border:1px solid #7ad03a; "' ) : '' ) . ' type="text" id="' . $this->product->get_key() . '_license" name="' . $this->product->get_key() . '_license" value="' . $value . '" /><a ' . ( ( 'valid' === $status ) ? ( 'style="color:#fff;background: #7ad03a; display: inline-block;text-decoration: none;font-size: 13px;line-height: 26px;height: 26px; margin-left:5px; padding: 0 10px 1px; -webkit-border-radius: 3px;border-radius: 3px; ">' . $valid_string ) : ( 'style="color:#fff;background: #dd3d36; display: inline-block;text-decoration: none;font-size: 13px;line-height: 26px;height: 26px; margin-left:5px; padding: 0 10px 1px; -webkit-border-radius: 3px;border-radius: 3px; ">' . $invalid_string ) ) . ' </a>&nbsp;&nbsp;&nbsp;<button name="' . $this->product->get_key() . '_btn_trigger" ' . ( ( 'valid' === $status ) ? ( ' class="button button-primary">' . $deactivate_string ) : ( ' class="button button-primary" value="yes" type="submit" >' . $activate_string ) ) . ' </button></p><p class="description">' . sprintf( $license_message, '<a href="' . $this->get_api_url() . '">' . $this->get_distributor_name() . '</a> ', $this->product->get_type() ) . '</p>';
153 + input.themeisle-sdk-license-input {
154 + width: 300px;
155 + padding: 0 8px;
156 + line-height: 2;
157 + min-height: 30px;
158 + }
114 159
160 + .themeisle-sdk-license-deactivate-cta {
161 + color: #fff;
162 + background: #7ad03a;
163 + display: inline-block;
164 + text-decoration: none;
165 + font-size: 13px;
166 + line-height: 30px;
167 + height: 26px;
168 + margin-left: 5px;
169 + padding: 0 10px 3px;
170 + -webkit-border-radius: 3px;
171 + border-radius: 3px;
172 + }
173 +
174 + .themeisle-sdk-license-activate-cta {
175 + color: #fff;
176 + background: #dd3d36;
177 + display: inline-block;
178 + text-decoration: none;
179 + font-size: 13px;
180 + line-height: 30px;
181 + height: 26px;
182 + margin-left: 5px;
183 + padding: 0 10px 3px;
184 + -webkit-border-radius: 3px;
185 + border-radius: 3px;
186 + }
187 +
188 + button.button.themeisle-sdk-licenser-button-cta {
189 + line-height: 26px;
190 + height: 29px;
191 + vertical-align: top;
192 + }
193 +
194 + </style>
195 + <?php
196 + echo sprintf(
197 + '<p>%s<input class="themeisle-sdk-license-input %s" type="text" id="%s_license" name="%s_license" value="%s" /><a class="%s">%s</a>&nbsp;&nbsp;&nbsp;<button name="%s_btn_trigger" class="button button-primary themeisle-sdk-licenser-button-cta" value="yes" type="submit" >%s</button></p><p class="description">%s</p>%s',
198 + ( ( 'valid' === $status ) ? sprintf( '<input type="hidden" value="%s" name="%s_license" />', esc_attr( $value ), esc_attr( $this->product->get_key() ) ) : '' ),
199 + ( ( 'valid' === $status ) ? 'themeisle-sdk-text-input-valid' : '' ),
200 + esc_attr( $this->product->get_key() ),
201 + esc_attr( ( ( 'valid' === $status ) ? $this->product->get_key() . '_mask' : $this->product->get_key() ) ),
202 + esc_attr( ( ( 'valid' === $status ) ? ( str_repeat( '*', 30 ) . substr( $value, - 5 ) ) : $value ) ),
203 + esc_attr( ( 'valid' === $status ? 'themeisle-sdk-license-deactivate-cta' : 'themeisle-sdk-license-activate-cta' ) ),
204 + esc_attr( 'valid' === $status ? $valid_string : $invalid_string ),
205 + esc_attr( $this->product->get_key() ),
206 + esc_attr( 'valid' === $status ? $deactivate_string : $activate_string ),
207 + sprintf( wp_kses_data( $license_message ), '<a href="' . esc_url( $this->get_api_url() ) . '">' . esc_attr( $this->get_distributor_name() ) . '</a> ', esc_attr( $this->product->get_type() ) ),
208 + wp_kses_data( empty( $error_message ) ? '' : sprintf( '<p style="color:#dd3d36">%s</p>', ( $error_message ) ) )
209 + ) . wp_nonce_field( $this->product->get_key() . 'nonce', $this->product->get_key() . 'nonce_field', false, false );//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
210 +
115 211 }
116 212
117 213 /**
118 214 * Return the license status.
119 215 *
216 + * @param bool $check_expiration Should check if license is valid, but expired.
217 + *
120 218 * @return string The License status.
121 219 */
122 - public function get_license_status() {
220 + public function get_license_status( $check_expiration = false ) {
123 221
124 222 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
125 223
126 224 if ( '' === $license_data ) {
@@ -125,14 +223,113 @@
125 223
126 224 if ( '' === $license_data ) {
127 225 return get_option( $this->product->get_key() . '_license_status', 'not_active' );
128 226 }
227 + $status = isset( $license_data->license ) ? $license_data->license : get_option( $this->product->get_key() . '_license_status', 'not_active' );
228 + if ( false === $check_expiration ) {
229 + return $status;
230 + }
129 231
130 - return isset( $license_data->license ) ? $license_data->license : get_option( $this->product->get_key() . '_license_status', 'not_active' );
232 + return ( 'valid' === $status && isset( $license_data->is_expired ) && 'yes' === $license_data->is_expired ) ? 'active_expired' : $status;
233 + }
131 234
235 + /**
236 + * Check status.
237 + *
238 + * @param string $product_file Product basefile.
239 + *
240 + * @return string Status license.
241 + */
242 + public static function status( $product_file ) {
243 + $product = Product::get( $product_file );
244 + if ( ! $product->requires_license() ) {
245 + return self::STATUS_VALID;
246 + }
247 + $license_data = self::get_license_data( $product->get_key() );
248 +
249 + $status = isset( $license_data->license ) ? $license_data->license : self::STATUS_NOT_ACTIVE;
250 +
251 + return ( 'valid' === $status && isset( $license_data->is_expired ) && 'yes' === $license_data->is_expired ) ? 'active_expired' : $status;
132 252 }
133 253
134 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 + /**
135 332 * Get remote api url.
136 333 *
137 334 * @return string Remote api url.
138 335 */
@@ -150,9 +347,9 @@
150 347 * @return string Remote api url.
151 348 */
152 349 public function get_distributor_name() {
153 350 if ( $this->is_from_partner( $this->product ) ) {
154 - return 'ThemeIsle';
351 + return 'Themeisle';
155 352 }
156 353
157 354 return $this->product->get_store_name();
158 355 }
@@ -157,21 +354,34 @@
157 354 return $this->product->get_store_name();
158 355 }
159 356
160 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 + /**
161 367 * Show the admin notice regarding the license status.
162 368 *
163 369 * @return bool Should we show the notice ?
164 370 */
165 - function show_notice() {
371 + public function show_notice() {
166 372 if ( ! is_admin() ) {
167 373 return false;
168 374 }
169 - $status = $this->get_license_status();
170 - $no_activations_string = apply_filters( $this->product->get_key() . '_lc_no_activations_string', 'No activations left for %s !!!. You need to upgrade your plan in order to use %s on more websites. Please ask the %s Staff for more details.' );
171 - $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>. ' );
172 - $expiration_string = apply_filters( $this->product->get_key() . '_lc_expiration_string', 'Your license is about to expire for %s. You can go to %s and renew it ' );
173 375
376 + if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
377 + return false;
378 + }
379 +
380 + $status = $this->get_license_status( true );
381 + $no_activations_string = apply_filters( $this->product->get_key() . '_lc_no_activations_string', Loader::$labels['licenser']['no_activations'] );
382 + $no_valid_string = apply_filters( $this->product->get_key() . '_lc_no_valid_string', sprintf( Loader::$labels['licenser']['inactive'], '%s', '<a href="%s" target="_blank">', '</a>', '<a href="%s">', '</a>' ) );
383 + $expired_license_string = apply_filters( $this->product->get_key() . '_lc_expired_heading_string', Loader::$labels['licenser']['expired'] );
174 384 // No activations left for this license.
175 385 if ( 'valid' != $status && $this->check_activation() ) {
176 386 ?>
177 387 <div class="error">
@@ -177,12 +387,12 @@
177 387 <div class="error">
178 388 <p><strong>
179 389 <?php
180 390 echo sprintf(
181 - $no_activations_string,
182 - $this->product->get_name(),
183 - $this->product->get_name(),
184 - '<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>'
185 395 );
186 396 ?>
187 397 </strong>
188 398 </p>
@@ -189,38 +399,91 @@
189 399 </div>
190 400 <?php
191 401 return false;
192 402 }
403 +
193 404 // Invalid license key.
194 - if ( 'valid' != $status ) {
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';
195 438 ?>
196 - <div class="error">
197 - <p>
198 - <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() ); ?> </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 ); ?>
199 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>
200 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>
201 472 <?php
202 473
203 474 return false;
204 475 }
205 -
206 - // Expired and soon to expire license.
207 - if ( 'valid' == $status && $this->check_expiration() ) {
476 + // Invalid license key.
477 + if ( 'valid' != $status ) {
208 478 ?>
209 - <div class="update-nag">
479 + <div class="error">
210 480 <p>
211 - <strong>
212 - <?php
213 - echo sprintf(
214 - $expiration_string,
215 - $this->product->get_name() . ' ' . $this->product->get_type(),
216 - '<a href="' . $this->renew_url() . '" target="_blank">' . $this->get_distributor_name() . '</a>'
217 - );
218 - ?>
219 - </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>
220 482 </p>
221 483 </div>
222 484 <?php
485 +
223 486 return false;
224 487 }
225 488
226 489 return true;
@@ -236,9 +499,9 @@
236 499 if ( '' === $license_data ) {
237 500 return false;
238 501 }
239 502
240 - return isset( $license_data->error ) ? ( 'no_activations_left' == $license_data->error ) : false;
503 + return isset( $license_data->license ) ? ( 'no_activations_left' == $license_data->license ) : false;
241 504
242 505 }
243 506
244 507 /**
@@ -245,9 +508,9 @@
245 508 * Check if the license is about to expire in the next month.
246 509 *
247 510 * @return bool
248 511 */
249 - function check_expiration() {
512 + public function check_expiration() {
250 513 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
251 514 if ( '' === $license_data ) {
252 515 return false;
253 516 }
@@ -265,9 +528,9 @@
265 528 * Return the renew url from the store used.
266 529 *
267 530 * @return string The renew url.
268 531 */
269 - function renew_url() {
532 + public function renew_url() {
270 533 $license_data = get_option( $this->product->get_key() . '_license_data', '' );
271 534 if ( '' === $license_data ) {
272 535 return $this->get_api_url();
273 536 }
@@ -274,9 +537,9 @@
274 537 if ( ! isset( $license_data->download_id ) || ! isset( $license_data->key ) ) {
275 538 return $this->get_api_url();
276 539 }
277 540
278 - 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;
279 542 }
280 543
281 544 /**
282 545 * Run the license check call.
@@ -281,9 +544,9 @@
281 544 /**
282 545 * Run the license check call.
283 546 */
284 547 public function product_valid() {
285 - 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
286 549 return;
287 550 }
288 551 $license = $this->check_license();
289 552 set_transient( $this->product->get_key() . '_license_data', $license, 12 * HOUR_IN_SECONDS );
@@ -296,84 +559,149 @@
296 559 * @return object The license data.
297 560 */
298 561 public function check_license() {
299 562 $status = $this->get_license_status();
300 - if ( 'not_active' == $status ) {
563 + if ( 'not_active' === $status ) {
301 564 $license_data = new \stdClass();
302 565 $license_data->license = 'not_active';
303 566
304 567 return $license_data;
305 568 }
306 - $license = trim( $this->license_key );
307 - $api_params = array(
308 - 'edd_action' => 'check_license',
309 - 'license' => $license,
310 - 'item_name' => rawurlencode( $this->product->get_name() ),
311 - 'url' => rawurlencode( home_url() ),
312 - );
313 - // Call the custom API.
314 - $response = wp_remote_get(
315 - add_query_arg( $api_params, $this->get_api_url() ),
316 - array(
317 - 'timeout' => 15,
318 - 'sslverify' => false,
319 - )
320 - );
569 + $license = trim( $this->license_key );
570 +
571 + $response = $this->do_license_process( $license, 'check' );
572 +
321 573 if ( is_wp_error( $response ) ) {
322 574 $license_data = new \stdClass();
323 - $license_data->license = 'valid';
324 -
575 + $license_data->license = 'invalid';
325 576 } else {
326 - $license_data = json_decode( wp_remote_retrieve_body( $response ) );
327 - if ( ! is_object( $license_data ) ) {
328 - $license_data = new \stdClass();
329 - $license_data->license = 'valid';
330 - }
577 + $license_data = $response;
331 578 }
579 +
332 580 $license_old = get_option( $this->product->get_key() . '_license_data', '' );
333 - 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 ) {
334 582 $this->increment_failed_checks();
583 +
584 + return $license_old;
585 + }
586 +
587 + if ( ! isset( $license_data->key ) ) {
588 + $license_data->key = isset( $license_old->key ) ? $license_old->key : '';
589 + }
590 + $this->reset_failed_checks();
591 +
592 + return $license_data;
593 +
594 + }
595 +
596 + /**
597 + * Do license activation/deactivation.
598 + *
599 + * @param string $license License key.
600 + * @param string $action What do to.
601 + *
602 + * @return bool|\WP_Error
603 + */
604 + public function do_license_process( $license, $action = 'toggle' ) {
605 + if ( strlen( $license ) < 10 ) {
606 + return new \WP_Error( 'themeisle-license-invalid-format', Loader::$labels['licenser']['invalid_msg'] );
607 + }
608 + $status = $this->get_license_status();
609 +
610 + if ( 'valid' === $status && 'activate' === $action ) {
611 + return new \WP_Error( 'themeisle-license-already-active', Loader::$labels['licenser']['already_active'] );
612 + }
613 + if ( 'valid' !== $status && 'deactivate' === $action ) {
614 + return new \WP_Error( 'themeisle-license-already-deactivate', Loader::$labels['licenser']['not_active'] );
615 + }
616 +
617 + if ( 'toggle' === $action ) {
618 + $action = ( 'valid' !== $status ? ( 'activate' ) : ( 'deactivate' ) );
619 + }
620 +
621 + // Call the custom API.
622 + if ( 'check' === $action ) {
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() ) );
335 624 } else {
336 - $this->reset_failed_checks();
625 + $response = wp_remote_post(
626 + sprintf( '%slicense/%s/%s/%s', Product::API_URL, $action, rawurlencode( $this->product->get_name() ), $license ),
627 + array(
628 + 'body' => wp_json_encode(
629 + array(
630 + 'url' => rawurlencode( home_url() ),
631 + )
632 + ),
633 + 'headers' => array(
634 + 'Content-Type' => 'application/json',
635 + ),
636 + )
637 + );
337 638 }
338 639
339 - if ( $this->failed_checks <= self::$max_failed ) {
340 - return $license_old;
640 + // make sure the response came back okay.
641 + if ( is_wp_error( $response ) ) {
642 + return new \WP_Error( 'themeisle-license-500', sprintf( Loader::$labels['licenser']['error_notice'], $response->get_error_message() ) );
341 643 }
342 644
343 - if ( isset( $license_old->hide_valid ) ) {
344 - $license_data->hide_valid = true;
645 + $license_data = json_decode( wp_remote_retrieve_body( $response ) );
646 +
647 + if ( ! is_object( $license_data ) ) {
648 + return new \WP_Error( 'themeisle-license-404', Loader::$labels['licenser']['error_notice2'] );
345 649 }
650 + if ( 'check' === $action ) {
651 + return $license_data;
652 + }
346 653
654 + Loader::clear_cache_token();
655 +
656 + if ( ! isset( $license_data->license ) ) {
657 + $license_data->license = 'invalid';
658 + }
659 +
347 660 if ( ! isset( $license_data->key ) ) {
348 - $license_data->key = isset( $license_old->key ) ? $license_old->key : '';
661 + $license_data->key = $license;
349 662 }
663 + if ( 'valid' === $license_data->license ) {
664 + $this->reset_failed_checks();
665 + }
350 666
351 - if ( isset( $license_old->hide_expiration ) ) {
352 - $license_data->hide_expiration = true;
667 + if ( 'deactivate' === $action ) {
668 +
669 + delete_option( $this->product->get_key() . '_license_data' );
670 + delete_option( $this->product->get_key() . '_license_plan' );
671 + delete_transient( $this->product->get_key() . '_license_data' );
672 +
673 + return true;
353 674 }
354 -
355 - if ( isset( $license_old->hide_activation ) ) {
356 - $license_data->hide_activation = true;
675 + if ( isset( $license_data->plan ) ) {
676 + update_option( $this->product->get_key() . '_license_plan', $license_data->plan );
357 677 }
678 + update_option( $this->product->get_key() . '_license_data', $license_data );
679 + set_transient( $this->product->get_key() . '_license_data', $license_data, 12 * HOUR_IN_SECONDS );
680 + if ( 'activate' === $action && 'valid' !== $license_data->license ) {
681 + return new \WP_Error( 'themeisle-license-invalid', Loader::$labels['licenser']['error_invalid'] );
682 + }
358 683
359 - return $license_data;
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 );
360 687
688 + return true;
361 689 }
362 690
363 691 /**
364 - * Increment the failed checks.
692 + * Reset the failed checks
365 693 */
366 - private function increment_failed_checks() {
367 - $this->failed_checks ++;
694 + private function reset_failed_checks() {
695 + $this->failed_checks = 1;
368 696 update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
369 697 }
370 698
371 699 /**
372 - * Reset the failed checks
700 + * Increment the failed checks.
373 701 */
374 - private function reset_failed_checks() {
375 - $this->failed_checks = 1;
702 + private function increment_failed_checks() {
703 + $this->failed_checks ++;
376 704 update_option( $this->product->get_key() . '_failed_checks', $this->failed_checks );
377 705 }
378 706
379 707 /**
@@ -378,64 +706,50 @@
378 706
379 707 /**
380 708 * Activate the license remotely.
381 709 */
382 - function activate_license() {
710 + public function process_license() {
383 711 // listen for our activate button to be clicked.
384 712 if ( ! isset( $_POST[ $this->product->get_key() . '_btn_trigger' ] ) ) {
385 713 return;
386 714 }
387 - $status = $this->get_license_status();
388 - // retrieve the license from the database.
389 - $license = $_POST[ $this->product->get_key() . '_license' ];
390 - $api_params = array(
391 - 'license' => $license,
392 - 'item_name' => rawurlencode( $this->product->get_name() ),
393 - 'url' => rawurlencode( home_url() ),
394 - );
395 - if ( 'valid' != $status ) {
396 - // data to send in our API request.
397 - $api_params['edd_action'] = 'activate_license';
398 - } else {
399 - $api_params['edd_action'] = 'deactivate_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;
400 719 }
401 - // Call the custom API.
402 - $response = wp_remote_get( add_query_arg( $api_params, $this->get_api_url() ) );
403 - // make sure the response came back okay.
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 +
727 + $response = $this->do_license_process( $license, 'toggle' );
404 728 if ( is_wp_error( $response ) ) {
405 - $license_data = new \stdClass();
406 - $license_data->license = ( 'valid' != $status ) ? 'valid' : 'invalid';
729 + $this->set_error( $response->get_error_message() );
407 730
408 - } else {
409 - $license_data = json_decode( wp_remote_retrieve_body( $response ) );
410 - if ( ! is_object( $license_data ) ) {
411 - $license_data = new \stdClass();
412 - $license_data->license = ( 'valid' != $status ) ? 'valid' : 'invalid';
413 - }
414 - if ( ! isset( $license_data->license ) ) {
415 - $license_data->license = 'invalid';
416 - }
731 + return;
417 732 }
418 - if ( ! isset( $license_data->key ) ) {
419 - $license_data->key = $license;
733 + if ( true === $response ) {
734 + $this->set_error( '' );
420 735 }
421 - if ( 'valid' == $license_data->license ) {
422 - $this->reset_failed_checks();
423 - }
736 + }
424 737
425 - if ( isset( $license_data->plan ) ) {
426 - update_option( $this->product->get_key() . '_license_plan', $license_data->plan );
427 - }
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 );
428 745
429 - update_option( $this->product->get_key() . '_license_data', $license_data );
430 - set_transient( $this->product->get_key() . '_license_data', $license_data, 12 * HOUR_IN_SECONDS );
431 -
432 746 }
433 747
434 748 /**
435 749 * Load the Themes screen.
436 750 */
437 - function load_themes_screen() {
751 + public function load_themes_screen() {
438 752 add_thickbox();
439 753 add_action( 'admin_notices', array( &$this, 'update_nag' ) );
440 754 }
441 755
@@ -441,31 +755,29 @@
441 755
442 756 /**
443 757 * Alter the nag for themes update.
444 758 */
445 - function update_nag() {
759 + public function update_nag() {
446 760 $theme = wp_get_theme( $this->product->get_slug() );
447 761 $api_response = get_transient( $this->product_key );
448 - if ( false === $api_response ) {
762 + if ( false === $api_response || ! isset( $api_response->new_version ) ) {
449 763 return;
450 764 }
451 765 $update_url = wp_nonce_url( 'update.php?action=upgrade-theme&amp;theme=' . urlencode( $this->product->get_slug() ), 'upgrade-theme_' . $this->product->get_slug() );
452 - $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'] );
453 767 $update_onclick = ' onclick="if ( confirm(\'' . esc_js( $update_message ) . '\') ) {return true;}return false;"';
454 768 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
455 769 echo '<div id="update-nag">';
456 770 printf(
457 - '<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>.',
458 - $theme->get( 'Name' ),
459 - $api_response->new_version,
460 - '#TB_inline?width=640&amp;inlineId=' . $this->product->get_version() . '_changelog',
461 - $theme->get( 'Name' ),
462 - $update_url,
463 - $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.
464 776 );
465 777 echo '</div>';
466 - echo '<div id="' . $this->product->get_slug() . '_' . 'changelog" style="display:none;">';
467 - 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'] ) );
468 780 echo '</div>';
469 781 }
470 782 }
471 783
@@ -475,14 +787,24 @@
475 787 * @param mixed $value The transient data.
476 788 *
477 789 * @return mixed
478 790 */
479 - function theme_update_transient( $value ) {
791 + public function theme_update_transient( $value ) {
480 792 $update_data = $this->check_for_update();
481 - if ( $update_data ) {
482 - $value->response[ $this->product->get_slug() ] = $update_data;
793 + if ( empty( $value ) ) {
794 + return $value;
483 795 }
484 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 +
485 807 return $value;
486 808 }
487 809
488 810 /**
@@ -489,9 +811,9 @@
489 811 * Check for updates
490 812 *
491 813 * @return array|bool Either the update data or false in case of failure.
492 814 */
493 - function check_for_update() {
815 + public function check_for_update() {
494 816 $update_data = get_transient( $this->product_key );
495 817
496 818 if ( false === $update_data ) {
497 819 $failed = false;
@@ -506,9 +828,9 @@
506 828 set_transient( $this->product_key, $data, 30 * MINUTE_IN_SECONDS );
507 829
508 830 return false;
509 831 }
510 - $update_data->sections = maybe_unserialize( $update_data->sections );
832 + $update_data->sections = isset( $update_data->sections ) ? maybe_unserialize( $update_data->sections ) : null;
511 833
512 834 set_transient( $this->product_key, $update_data, 12 * HOUR_IN_SECONDS );
513 835 }
514 836 if ( ! isset( $update_data->new_version ) ) {
@@ -526,23 +848,21 @@
526 848 *
527 849 * @return bool|mixed Update api response.
528 850 */
529 851 private function get_version_data() {
530 - $api_params = array(
531 - 'edd_action' => 'get_version',
532 - 'version' => $this->product->get_version(),
533 - 'license' => $this->license_key,
534 - 'name' => $this->product->get_name(),
535 - 'slug' => $this->product->get_slug(),
536 - 'author' => $this->get_distributor_name(),
537 - 'url' => rawurlencode( home_url() ),
538 - );
539 - $response = wp_remote_get(
540 - $this->get_api_url(),
852 +
853 + $response = $this->safe_get(
854 + sprintf(
855 + '%slicense/version/%s/%s/%s/%s',
856 + Product::API_URL,
857 + rawurlencode( $this->product->get_name() ),
858 + ( empty( $this->license_key ) ? 'free' : $this->license_key ),
859 + $this->product->get_version(),
860 + rawurlencode( home_url() )
861 + ),
541 862 array(
542 - 'timeout' => 15,
863 + 'timeout' => 15, //phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout, Inherited by wp_remote_get only, for vip environment we use defaults.
543 864 'sslverify' => false,
544 - 'body' => $api_params,
545 865 )
546 866 );
547 867 if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
548 868 return false;
@@ -550,8 +870,17 @@
550 870 $update_data = json_decode( wp_remote_retrieve_body( $response ) );
551 871 if ( ! is_object( $update_data ) ) {
552 872 return false;
553 873 }
874 + if ( isset( $update_data->slug ) ) {
875 + $update_data->slug = $this->product->get_slug();
876 + }
877 + if ( isset( $update_data->icons ) ) {
878 + $update_data->icons = (array) $update_data->icons;
879 + }
880 + if ( isset( $update_data->banners ) ) {
881 + $update_data->banners = (array) $update_data->banners;
882 + }
554 883
555 884 return $update_data;
556 885 }
557 886
@@ -557,10 +886,10 @@
557 886
558 887 /**
559 888 * Delete the update transient
560 889 */
561 - function delete_theme_update_transient() {
562 - delete_transient( $this->product_key );
890 + public function delete_theme_update_transient() {
891 + return delete_transient( $this->product_key );
563 892 }
564 893
565 894 /**
566 895 * Check for Updates at the defined API endpoint and modify the update array.
@@ -574,12 +903,21 @@
574 903 $this->do_check = true;
575 904
576 905 return $_transient_data;
577 906 }
907 + $this->product->setup_from_fileheaders();
578 908 $api_response = $this->api_request();
579 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 + }
580 914 if ( version_compare( $this->product->get_version(), $api_response->new_version, '<' ) ) {
581 - $_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 ] );
582 920 }
583 921 }
584 922
585 923 return $_transient_data;
@@ -614,9 +952,9 @@
614 952 *
615 953 * @return object $_data
616 954 */
617 955 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
618 - if ( ( 'plugin_information' != $_action ) || ! isset( $_args->slug ) || ( $_args->slug != $this->product->get_slug() ) ) {
956 + if ( ( 'plugin_information' !== $_action ) || ! isset( $_args->slug ) || ( $_args->slug !== $this->product->get_slug() ) ) {
619 957 return $_data;
620 958 }
621 959 $api_response = $this->api_request();
622 960 if ( false !== $api_response ) {
@@ -633,9 +971,9 @@
633 971 * @param string $url Url to check.
634 972 *
635 973 * @return array $array
636 974 */
637 - function http_request_args( $args, $url ) {
975 + public function http_request_args( $args, $url ) {
638 976 // If it is an https request and we are performing a package download, disable ssl verification.
639 977 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
640 978 $args['sslverify'] = false;
641 979 }
@@ -651,9 +989,9 @@
651 989 * @return bool Should we load the module?
652 990 */
653 991 public function can_load( $product ) {
654 992
655 - if ( $product->is_wordpress_available() ) {
993 + if ( ! $product->requires_license() ) {
656 994 return false;
657 995 }
658 996
659 997 return ( apply_filters( $product->get_key() . '_enable_licenser', true ) === true );
@@ -676,29 +1014,85 @@
676 1014 if ( $this->product->requires_license() ) {
677 1015 $this->failed_checks = intval( get_option( $this->product->get_key() . '_failed_checks', 0 ) );
678 1016 $this->register_license_hooks();
679 1017 }
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 + }
1025 + $namespace = apply_filters( 'themesle_sdk_namespace_' . md5( $product->get_basefile() ), false );
680 1026
1027 + if ( false !== $namespace ) {
1028 + $this->namespace = $namespace;
1029 + add_filter( 'themeisle_sdk_license_process_' . $namespace, [ $this, 'do_license_process' ], 10, 2 );
1030 + add_filter( 'product_' . $namespace . '_license_status', [ $this, 'get_license_status' ], PHP_INT_MAX );
1031 + add_filter( 'product_' . $namespace . '_license_key', [ $this->product, 'get_license' ] );
1032 + add_filter( 'product_' . $namespace . '_license_plan', [ $this, 'get_plan' ], PHP_INT_MAX );
1033 + if ( defined( 'WP_CLI' ) && WP_CLI ) {
1034 + \WP_CLI::add_command( $namespace . ' activate', [ $this, 'cli_activate' ] );
1035 + \WP_CLI::add_command( $namespace . ' deactivate', [ $this, 'cli_deactivate' ] );
1036 + \WP_CLI::add_command( $namespace . ' is-active', [ $this, 'cli_is_active' ] );
1037 + }
1038 + }
1039 +
1040 + add_action( 'admin_head', [ $this, 'auto_activate' ] );
681 1041 if ( $this->product->is_plugin() ) {
682 - add_filter(
683 - 'pre_set_site_transient_update_plugins',
684 - [
685 - $this,
686 - 'pre_set_site_transient_update_plugins_filter',
687 - ]
688 - );
689 - add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
690 - 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>';
691 1069
1070 + return $new_actions;
1071 + }
1072 + );
1073 + }
1074 + }
1075 +
692 1076 return $this;
693 1077 }
694 1078 if ( $this->product->is_theme() ) {
695 - add_filter( 'site_transient_update_themes', array( &$this, 'theme_update_transient' ) );
696 - add_filter( 'delete_site_transient_update_themes', array( &$this, 'delete_theme_update_transient' ) );
697 - add_action( 'load-update-core.php', array( &$this, 'delete_theme_update_transient' ) );
698 - add_action( 'load-themes.php', array( &$this, 'delete_theme_update_transient' ) );
699 - add_action( 'load-themes.php', array( &$this, 'load_themes_screen' ) );
700 - 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 + }
701 1095
702 1096 return $this;
703 1097
704 1098 }
@@ -710,10 +1104,180 @@
710 1104 * Register license fields for the products.
711 1105 */
712 1106 public function register_license_hooks() {
713 1107 add_action( 'admin_init', array( $this, 'register_settings' ) );
714 - add_action( 'admin_init', array( $this, 'activate_license' ) );
1108 + add_action( 'admin_init', array( $this, 'process_license' ) );
715 1109 add_action( 'admin_init', array( $this, 'product_valid' ), 99999999 );
716 1110 add_action( 'admin_notices', array( $this, 'show_notice' ) );
717 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' );
1121 + }
1122 +
1123 + if ( ! current_user_can( 'manage_options' ) ) {
1124 + wp_send_json_error( 'Insufficient permissions' );
1125 + }
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 +
1147 + $license_file = dirname( $this->product->get_basefile() ) . '/license.json';
1148 +
1149 + global $wp_filesystem;
1150 + if ( ! is_file( $license_file ) ) {
1151 + return false;
1152 + }
1153 +
1154 + require_once ABSPATH . '/wp-admin/includes/file.php';
1155 + \WP_Filesystem();
1156 + $content = json_decode( $wp_filesystem->get_contents( $license_file ) );
1157 + if ( ! is_object( $content ) ) {
1158 + return false;
1159 + }
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 ) ) {
1184 + return;
1185 + }
1186 +
1187 +
1188 + $this->license_local = $license_key;
1189 + $lock_key = $this->product->get_key() . '_autoactivated';
1190 +
1191 + if ( 'yes' === get_option( $lock_key, '' ) ) {
1192 + return;
1193 + }
1194 + if ( 'yes' === get_transient( $lock_key ) ) {
1195 + return;
1196 + }
1197 + $response = $this->do_license_process( $license_key, 'activate' );
1198 +
1199 + set_transient( $lock_key, 'yes', 6 * HOUR_IN_SECONDS );
1200 +
1201 + if ( apply_filters( $this->product->get_key() . '_hide_license_notices', false ) ) {
1202 + return;
1203 + }
1204 +
1205 + if ( true === $response ) {
1206 + add_action( 'admin_notices', [ $this, 'autoactivate_notice' ] );
1207 + }
1208 + }
1209 +
1210 + /**
1211 + * Show auto-activate notice.
1212 + */
1213 + public function autoactivate_notice() {
1214 + ?>
1215 + <div class="notice notice-success is-dismissible">
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>
1217 + </div>
1218 + <?php
1219 + }
1220 +
1221 + /**
1222 + * Activate product license on this site.
1223 + *
1224 + * ## OPTIONS
1225 + *
1226 + * @param array $args Command args.
1227 + *
1228 + * [<license-key>]
1229 + * : Product license key.
1230 + */
1231 + public function cli_activate( $args ) {
1232 + $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1233 + $response = $this->do_license_process( $license_key, 'activate' );
1234 + if ( true !== $response ) {
1235 + \WP_CLI::error( $response->get_error_message() );
1236 +
1237 + return;
1238 + }
1239 +
1240 + \WP_CLI::success( 'Product successfully activated.' );
1241 + }
1242 +
1243 + /**
1244 + * Deactivate product license on this site.
1245 + *
1246 + * @param array $args Command args.
1247 + *
1248 + * ## OPTIONS
1249 + *
1250 + * [<license-key>]
1251 + * : Product license key.
1252 + */
1253 + public function cli_deactivate( $args ) {
1254 + $license_key = isset( $args[0] ) ? trim( $args[0] ) : '';
1255 + $response = $this->do_license_process( $license_key, 'deactivate' );
1256 + if ( true !== $response ) {
1257 + \WP_CLI::error( $response->get_error_message() );
1258 +
1259 + return;
1260 + }
1261 +
1262 + \WP_CLI::success( 'Product successfully deactivated.' );
1263 + }
1264 +
1265 + /**
1266 + * Checks if product has license activated.
1267 + *
1268 + * @param array $args Command args.
1269 + *
1270 + * @subcommand is-active
1271 + */
1272 + public function cli_is_active( $args ) {
1273 +
1274 + $status = $this->get_license_status();
1275 + if ( 'valid' === $status ) {
1276 + \WP_CLI::halt( 0 );
1277 +
1278 + return;
1279 + }
1280 +
1281 + \WP_CLI::halt( 1 );
718 1282 }
719 1283 }