PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.6.4
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.6.4
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / classes / Plugin / License.php

License.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.6.4, at classes/Plugin/License.php

526 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Upgrade management.
4 *
5 * @copyright (c) 2023, Code Atlantic LLC.
6 *
7 * @package ContentControl
8 */
9
10 namespace ContentControl\Plugin;
11
12 defined( 'ABSPATH' ) || exit;
13
14 /**
15 * License management.
16 *
17 * NOTE: For wordpress.org admins: This is only used if:
18 * - The user explicitly entered a license key.
19 *
20 * @package ContentControl
21 */
22 class License {
23
24 /**
25 * EDD API URL.
26 *
27 * @var string
28 */
29 const API_URL = 'https://contentcontrolplugin.com/edd-sl-api/';
30
31 /**
32 * Item ID.
33 *
34 * @var int
35 */
36 const ID = 45;
37
38 /**
39 * Option key.
40 *
41 * @var string
42 */
43 const OPTION_KEY = 'content_control_license';
44
45 /**
46 * License data
47 *
48 * @var array<string,mixed>|null
49 */
50 private $license_data;
51
52 /**
53 * Initialize license management.
54 */
55 public function __construct() {
56 $this->register_hooks();
57 }
58
59 /**
60 * Register hooks.
61 *
62 * @return void
63 */
64 public function register_hooks() {
65 add_action( 'init', [ $this, 'autoregister' ] );
66 add_action( 'content_control_license_status_check', [ $this, 'refresh_license_status' ] );
67 add_action( 'admin_init', [ $this, 'schedule_crons' ] );
68 }
69
70 /**
71 * Autoregister license.
72 *
73 * @return void
74 */
75 public function autoregister() {
76 $key = defined( '\CONTENT_CONTROL_LICENSE_KEY' ) && '' !== \CONTENT_CONTROL_LICENSE_KEY ? \CONTENT_CONTROL_LICENSE_KEY : false;
77
78 if ( $key && '' === $this->get_license_key() ) {
79 try {
80 $this->activate_license( \CONTENT_CONTROL_LICENSE_KEY );
81 // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
82 } catch ( \Exception $e ) {
83 // Do nothing.
84 }
85 }
86 }
87
88 /**
89 * Schedule cron jobs.
90 *
91 * @return void
92 */
93 public function schedule_crons() {
94 if ( ! wp_next_scheduled( 'content_control_license_status_check' ) ) {
95 wp_schedule_event( time(), 'daily', 'content_control_license_status_check' );
96 }
97 }
98
99 /**
100 * Get license data.
101 *
102 * @return array{key:string|null,status:array<string,mixed>|null}
103 */
104 public function get_license_data() {
105 if ( ! isset( $this->license_data ) ) {
106 $this->license_data = \get_option( self::OPTION_KEY, [
107 'key' => '',
108 'status' => [
109 'success' => false, // bool.
110 'license' => 'invalid', // valid or invalid.
111 'item_id' => self::ID, // int or false.
112 'item_name' => '', // string.
113 'license_limit' => 1, // int. 0 = unlimited.
114 'site_count' => 0,
115 'expires' => '', // date or 'lifetime'.
116 'activations_left' => 0, // int or 'unlimited'.
117 'checksum' => '', // string.
118 'payment_id' => 0, // int.
119 'customer_name' => '', // string.
120 'customer_email' => '', // string.
121 'price_id' => 0, // string or int.
122 'error' => null, // string. expired, missing, invalid, item_name_mismatch, no_activations_left etc.
123 ],
124 ] );
125 }
126
127 return $this->license_data;
128 }
129
130 /**
131 * Get license key.
132 *
133 * @return string
134 */
135 public function get_license_key() {
136 $license_data = $this->get_license_data();
137 return ! empty( $license_data['key'] ) ? $license_data['key'] : '';
138 }
139
140 /**
141 * Get license status.
142 *
143 * @param bool $refresh Whether to refresh license status.
144 *
145 * @return array<string,mixed> Array of license status data.
146 */
147 public function get_license_status( $refresh = false ) {
148 if ( $refresh ) {
149 $this->refresh_license_status();
150 }
151
152 $license_data = $this->get_license_data();
153
154 $license_status = isset( $license_data['status'] ) ? $license_data['status'] : [];
155
156 return $license_status;
157 }
158
159 /**
160 * Get license level.
161 *
162 * Only used in pro version.
163 *
164 * @return int Integer representing license level.
165 */
166 public function get_license_level() {
167 $license_status = $this->get_license_status();
168
169 if ( empty( $license_status ) ) {
170 return -1;
171 }
172
173 $price_id = isset( $license_status['price_id'] ) ? $license_status['price_id'] : null;
174
175 switch ( $price_id ) {
176 default:
177 return -1;
178
179 case false:
180 case 0:
181 return 0;
182
183 case 1:
184 case 2:
185 case 3:
186 case 4:
187 return absint( $price_id );
188 }
189 }
190
191 /**
192 * Update license data.
193 *
194 * @param array{key:string|null,status:array<string,mixed>|null} $license_data License data.
195 *
196 * @return bool
197 */
198 public function udpate_license_data( $license_data ) {
199 if ( \update_option( self::OPTION_KEY, $license_data ) ) {
200 $this->license_data = $license_data;
201 return true;
202 }
203
204 return false;
205 }
206
207 /**
208 * Update license key.
209 *
210 * @param string $key License key.
211 *
212 * @return void
213 */
214 public function update_license_key( $key ) {
215 $license_data = $this->get_license_data();
216
217 $old_key = isset( $license_data['key'] ) ? $license_data['key'] : '';
218
219 $license_data['key'] = trim( $key );
220
221 $this->udpate_license_data( $license_data );
222
223 /**
224 * Fires when license key is changed.
225 *
226 * @param string $key License key.
227 * @param string $old_key Old license key.
228 */
229 \do_action( 'content_control_license_key_changed', $key, $old_key );
230 }
231
232 /**
233 * Update license status.
234 *
235 * @param array<string,mixed> $license_status License status data.
236 *
237 * @return void
238 */
239 public function update_license_status( $license_status ) {
240 $license_data = $this->get_license_data();
241
242 $previous_status = isset( $license_data['status'] ) ? $license_data['status'] : [];
243
244 if ( ! empty( $license_status['error'] ) ) {
245 $license_status['error_message'] = $this->get_license_error_message( $license_status );
246 }
247
248 $license_data['status'] = $license_status;
249
250 $this->udpate_license_data( $license_data );
251
252 /**
253 * Fires when license status is updated.
254 *
255 * @param array $license_status License status data.
256 * @param array $previous_status Previous license status data.
257 */
258 \do_action( 'content_control_license_status_updated', $license_status, $previous_status );
259 }
260
261 /**
262 * Get license expiration from license status data.
263 *
264 * @param bool $as_datetime Whether to return as DateTime object.
265 *
266 * @return \DateTime|false|null
267 */
268 public function get_license_expiration( $as_datetime = false ) {
269 $status = $this->get_license_status();
270
271 $expiration = isset( $status['expires'] ) ? $status['expires'] : null;
272
273 return $as_datetime ?
274 \DateTime::createFromFormat( 'Y-m-d H:i:s', $expiration ) :
275 $expiration;
276 }
277
278 /**
279 * Fetch license status from remote server.
280 * This is a blocking request.
281 *
282 * @return void
283 */
284 public function refresh_license_status() {
285 $key = $this->get_license_key();
286
287 if ( empty( $key ) ) {
288 return;
289 }
290
291 try {
292 $status = $this->check_license();
293 } catch ( \Exception $e ) {
294 $status = [];
295 }
296
297 $this->update_license_status( $status );
298 }
299
300 /**
301 * Get license status from remote server.
302 *
303 * @return array<string,mixed> License status data.
304 *
305 * @throws \Exception If there is an error.
306 */
307 private function check_license() {
308 $api_params = [
309 'edd_action' => 'check_license',
310 'license' => $this->get_license_key(),
311 'item_id' => self::ID,
312 'item_name' => rawurlencode( 'Content Control Pro' ),
313 'url' => home_url(),
314 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
315 ];
316
317 // Call the custom API.
318 $response = wp_remote_post(
319 self::API_URL,
320 [
321 'timeout' => 15,
322 'sslverify' => false,
323 'body' => $api_params,
324 ]
325 );
326
327 if ( is_wp_error( $response ) ) {
328 throw new \Exception( esc_html( $response->get_error_message() ) );
329 }
330
331 $license_status = json_decode( wp_remote_retrieve_body( $response ), true );
332
333 if ( empty( $license_status ) ) {
334 return [];
335 }
336
337 return $license_status;
338 }
339
340 /**
341 * Activate license.
342 *
343 * @param string $key License key.
344 *
345 * @return array<string,mixed> License status data.
346 *
347 * @throws \Exception If there is an error.
348 */
349 public function activate_license( $key = null ) {
350 if ( ! empty( $key ) ) {
351 $this->update_license_key( $key );
352 } else {
353 $key = $this->get_license_key();
354 }
355
356 $api_params = [
357 'edd_action' => 'activate_license',
358 'license' => $key,
359 'item_id' => self::ID,
360 'url' => home_url(),
361 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
362 ];
363
364 // Call the custom API.
365 $response = wp_remote_post(
366 self::API_URL,
367 [
368 'timeout' => 15,
369 'sslverify' => false,
370 'body' => $api_params,
371 ]
372 );
373
374 // Make sure the response came back okay.
375 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
376 if ( is_wp_error( $response ) ) {
377 $message = $response->get_error_message();
378 } else {
379 $message = __( 'An error occurred, please try again.', 'content-control' );
380 }
381
382 throw new \Exception( esc_html( $message ) );
383 }
384
385 $license_status = json_decode( wp_remote_retrieve_body( $response ), true );
386
387 $this->update_license_status( $license_status );
388
389 if ( $this->is_license_active() ) {
390 if ( ! \get_option( 'content_control_pro_activation_date', false ) ) {
391 \update_option( 'content_control_pro_activation_date', time() );
392 }
393 }
394
395 return $this->get_license_status();
396 }
397
398 /**
399 * Deactivate license.
400 *
401 * @return array<string,mixed> License status data.
402 *
403 * @throws \Exception If there is an error.
404 */
405 public function deactivate_license() {
406 $api_params = [
407 'edd_action' => 'deactivate_license',
408 'license' => $this->get_license_key(),
409 'item_id' => self::ID,
410 'item_name' => rawurlencode( 'Content Control' ),
411 'url' => home_url(),
412 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
413 ];
414
415 // Call the custom API.
416 $response = wp_remote_post(
417 self::API_URL,
418 [
419 'timeout' => 15,
420 'sslverify' => false,
421 'body' => $api_params,
422 ]
423 );
424
425 // Make sure the response came back okay.
426 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
427 if ( is_wp_error( $response ) ) {
428 $message = $response->get_error_message();
429 } else {
430 $message = __( 'An error occurred, please try again.', 'content-control' );
431 }
432
433 throw new \Exception( esc_html( $message ) );
434 }
435
436 $license_status = json_decode( wp_remote_retrieve_body( $response ), true );
437
438 $this->update_license_status( $license_status );
439
440 return $license_status;
441 }
442
443 /**
444 * Convert license error to human readable message.
445 *
446 * @param array<string,mixed> $license_status License status data.
447 *
448 * @return string
449 */
450 public function get_license_error_message( $license_status ) {
451 switch ( $license_status['error'] ) {
452 case 'expired':
453 $message = sprintf(
454 /* translators: the license key expiration date */
455 __( 'Your license key expired on %s.', 'content-control' ),
456 date_i18n( \get_option( 'date_format' ), strtotime( $license_status['expires'], time() ) )
457 );
458 break;
459
460 case 'disabled':
461 case 'revoked':
462 $message = __( 'Your license key has been disabled.', 'content-control' );
463 break;
464
465 case 'missing':
466 $message = __( 'Invalid license.', 'content-control' );
467 break;
468
469 case 'invalid':
470 case 'site_inactive':
471 $message = __( 'Your license is not active for this URL.', 'content-control' );
472 break;
473
474 case 'item_name_mismatch':
475 $message = sprintf(
476 /* translators: the plugin name */
477 __( 'This appears to be an invalid license key for %s.', 'content-control' ),
478 __( 'Content Control', 'content-control' )
479 );
480 break;
481
482 case 'no_activations_left':
483 $message = __( 'Your license key has reached its activation limit.', 'content-control' );
484 break;
485
486 default:
487 $message = __( 'An error occurred, please try again.', 'content-control' );
488 break;
489 }
490
491 return $message;
492 }
493
494 /**
495 * Remove license.
496 *
497 * @return void
498 */
499 public function remove_license() {
500 try {
501 $deactivated = $this->deactivate_license();
502 } catch ( \Exception $e ) {
503 $deactivated = [];
504 }
505
506 if ( ! empty( $deactivated['license'] ) && 'active' !== $deactivated['license'] ) {
507 \delete_option( self::OPTION_KEY );
508 }
509 }
510
511 /**
512 * Check if license is active.
513 *
514 * @return bool
515 */
516 public function is_license_active() {
517 $license_status = $this->get_license_status();
518
519 if ( empty( $license_status ) ) {
520 return false;
521 }
522
523 return 'valid' === $license_status['license'];
524 }
525 }
526