PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 8.5.4
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v8.5.4
9.1.2 9.1.1 9.1.0 9.0.3 9.0.2 9.0.1 9.0.0 8.5.79 8.5.78 8.5.77 8.5.76 8.5.75 8.5.74 8.5.73 8.5.72 8.5.71 8.5.70 8.5.69 8.5.68 8.5.35 8.5.36 8.5.37 8.5.38 8.5.39 8.5.4 All 221 releases
wpvr / appsero / src / License.php

License.php in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 8.5.4, at appsero/src/License.php

810 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Appsero;
4
5 /**
6 * Appsero License Checker
7 *
8 * This class will check, active and deactive license
9 */
10 class License {
11
12 /**
13 * AppSero\Client
14 *
15 * @var object
16 */
17 protected $client;
18
19 /**
20 * Arguments of create menu
21 *
22 * @var array
23 */
24 protected $menu_args;
25
26 /**
27 * `option_name` of `wp_options` table
28 *
29 * @var string
30 */
31 protected $option_key;
32
33 /**
34 * Error message of HTTP request
35 *
36 * @var string
37 */
38 public $error;
39
40 /**
41 * Success message on form submit
42 *
43 * @var string
44 */
45 public $success;
46
47 /**
48 * Corn schedule hook name
49 *
50 * @var string
51 */
52 protected $schedule_hook;
53
54 /**
55 * Set value for valid license
56 *
57 * @var bool
58 */
59 private $is_valid_license = null;
60
61 /**
62 * Initialize the class
63 *
64 * @param Appsero\Client
65 */
66 public function __construct( Client $client ) {
67 $this->client = $client;
68
69 $this->option_key = 'appsero_' . md5( $this->client->slug ) . '_manage_license';
70
71 $this->schedule_hook = $this->client->slug . '_license_check_event';
72
73 // Creating WP Ajax Endpoint to refresh license remotely
74 add_action( "wp_ajax_appsero_refresh_license_" . $this->client->hash, array( $this, 'refresh_license_api' ) );
75
76 // Run hook to check license status daily
77 add_action( $this->schedule_hook, array( $this, 'check_license_status' ) );
78
79 // Active/Deactive corn schedule
80 $this->run_schedule();
81 }
82
83 /**
84 * Set the license option key.
85 *
86 * If someone wants to override the default generated key.
87 *
88 * @param string $key
89 *
90 * @since 1.3.0
91 *
92 * @return License
93 */
94 public function set_option_key( $key ) {
95 $this->option_key = $key;
96
97 return $this;
98 }
99
100 /**
101 * Get the license key
102 *
103 * @since 1.3.0
104 *
105 * @return string|null
106 */
107 public function get_license() {
108 return get_option( $this->option_key, null );
109 }
110
111 /**
112 * Check license
113 *
114 * @return bool
115 */
116 public function check( $license_key ) {
117 $route = 'public/license/' . $this->client->hash . '/check';
118
119 return $this->send_request( $license_key, $route );
120 }
121
122 /**
123 * Active a license
124 *
125 * @return bool
126 */
127 public function activate( $license_key ) {
128 $route = 'public/license/' . $this->client->hash . '/activate';
129
130 return $this->send_request( $license_key, $route );
131 }
132
133 /**
134 * Deactivate a license
135 *
136 * @return bool
137 */
138 public function deactivate( $license_key ) {
139 $route = 'public/license/' . $this->client->hash . '/deactivate';
140
141 return $this->send_request( $license_key, $route );
142 }
143
144 /**
145 * Send common request
146 *
147 * @param $license_key
148 * @param $route
149 *
150 * @return array
151 */
152 protected function send_request( $license_key, $route ) {
153 $params = array(
154 'license_key' => $license_key,
155 'url' => esc_url( home_url() ),
156 'is_local' => $this->client->is_local_server(),
157 );
158
159 $response = $this->client->send_request( $params, $route, true );
160
161 if ( is_wp_error( $response ) ) {
162 return array(
163 'success' => false,
164 'error' => $response->get_error_message()
165 );
166 }
167
168 $response = json_decode( wp_remote_retrieve_body( $response ), true );
169
170 if ( empty( $response ) || isset( $response['exception'] )) {
171 return array(
172 'success' => false,
173 'error' => $this->client->__trans( 'Unknown error occurred, Please try again.' ),
174 );
175 }
176
177 if ( isset( $response['errors'] ) && isset( $response['errors']['license_key'] ) ) {
178 $response = array(
179 'success' => false,
180 'error' => $response['errors']['license_key'][0]
181 );
182 }
183
184 return $response;
185 }
186
187 /**
188 * License Refresh Endpoint
189 */
190 public function refresh_license_api() {
191 $this->check_license_status();
192
193 return wp_send_json(
194 array(
195 'message' => 'License refreshed successfully.'
196 ),
197 200
198 );
199 }
200
201 /**
202 * Add settings page for license
203 *
204 * @param array $args
205 *
206 * @return void
207 */
208 public function add_settings_page( $args = array() ) {
209 $defaults = array(
210 'type' => 'menu', // Can be: menu, options, submenu
211 'page_title' => 'Manage License',
212 'menu_title' => 'Manage License',
213 'capability' => 'manage_options',
214 'menu_slug' => $this->client->slug . '-manage-license',
215 'icon_url' => '',
216 'position' => null,
217 'parent_slug' => '',
218 );
219
220 $this->menu_args = wp_parse_args( $args, $defaults );
221
222 add_action( 'admin_menu', array( $this, 'admin_menu' ), 99 );
223 }
224
225 /**
226 * Admin Menu hook
227 *
228 * @return void
229 */
230 public function admin_menu() {
231 switch ( $this->menu_args['type'] ) {
232 case 'menu':
233 $this->create_menu_page();
234 break;
235
236 case 'submenu':
237 $this->create_submenu_page();
238 break;
239
240 case 'options':
241 $this->create_options_page();
242 break;
243 }
244 }
245
246 /**
247 * License menu output
248 */
249 public function menu_output() {
250 if ( isset( $_POST['submit'] ) ) {
251 $this->license_form_submit( $_POST );
252 }
253
254 $license = $this->get_license();
255 $action = ( $license && isset( $license['status'] ) && 'activate' == $license['status'] ) ? 'deactive' : 'active';
256 $this->licenses_style();
257 ?>
258
259 <div class="wrap appsero-license-settings-wrapper">
260 <h1>License Settings</h1>
261
262 <?php
263 $this->show_license_page_notices();
264 do_action( 'before_appsero_license_section' );
265 ?>
266
267 <div class="appsero-license-settings appsero-license-section">
268 <?php $this->show_license_page_card_header( $license ); ?>
269
270 <div class="appsero-license-details">
271 <p>
272 <?php printf( $this->client->__trans( 'Activate <strong>%s</strong> by your license key to get professional support and automatic update from your WordPress dashboard.' ), $this->client->name ); ?>
273 </p>
274 <form method="post" action="<?php $this->form_action_url(); ?>" novalidate="novalidate" spellcheck="false">
275 <input type="hidden" name="_action" value="<?php echo $action; ?>">
276 <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
277 <div class="license-input-fields">
278 <div class="license-input-key">
279 <svg enable-background="new 0 0 512 512" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
280 <path d="m463.75 48.251c-64.336-64.336-169.01-64.335-233.35 1e-3 -43.945 43.945-59.209 108.71-40.181 167.46l-185.82 185.82c-2.813 2.813-4.395 6.621-4.395 10.606v84.858c0 8.291 6.709 15 15 15h84.858c3.984 0 7.793-1.582 10.605-4.395l21.211-21.226c3.237-3.237 4.819-7.778 4.292-12.334l-2.637-22.793 31.582-2.974c7.178-0.674 12.847-6.343 13.521-13.521l2.974-31.582 22.793 2.651c4.233 0.571 8.496-0.85 11.704-3.691 3.193-2.856 5.024-6.929 5.024-11.206v-27.929h27.422c3.984 0 7.793-1.582 10.605-4.395l38.467-37.958c58.74 19.043 122.38 4.929 166.33-39.046 64.336-64.335 64.336-169.01 0-233.35zm-42.435 106.07c-17.549 17.549-46.084 17.549-63.633 0s-17.549-46.084 0-63.633 46.084-17.549 63.633 0 17.548 46.084 0 63.633z"/>
281 </svg>
282 <input type="text" value="<?php echo $this->get_input_license_value( $action, $license ); ?>"
283 placeholder="<?php echo esc_attr( $this->client->__trans( 'Enter your license key to activate' ) ); ?>" name="license_key"
284 <?php echo ( 'deactive' == $action ) ? 'readonly="readonly"' : ''; ?>
285 />
286 </div>
287 <button type="submit" name="submit" class="<?php echo 'deactive' == $action ? 'deactive-button' : ''; ?>">
288 <?php echo $action == 'active' ? $this->client->__trans( 'Activate License' ) : $this->client->__trans( 'Deactivate License' ); ?>
289 </button>
290 </div>
291 </form>
292
293 <?php
294 if ( 'deactive' == $action && isset( $license['remaining'] ) ) {
295 $this->show_active_license_info( $license );
296 } ?>
297 </div>
298 </div> <!-- /.appsero-license-settings -->
299
300 <?php do_action( 'after_appsero_license_section' ); ?>
301 </div>
302 <?php
303 }
304
305 /**
306 * License form submit
307 */
308 public function license_form_submit( $form ) {
309 if ( ! isset( $form['_nonce'], $form['_action'] ) ) {
310 $this->error = $this->client->__trans( 'Please add all information' );
311
312 return;
313 }
314
315 if ( ! wp_verify_nonce( $form['_nonce'], $this->client->name ) ) {
316 $this->error = $this->client->__trans( "You don't have permission to manage license." );
317
318 return;
319 }
320
321 switch ( $form['_action'] ) {
322 case 'active':
323 $this->active_client_license( $form );
324 break;
325
326 case 'deactive':
327 $this->deactive_client_license( $form );
328 break;
329
330 case 'refresh':
331 $this->refresh_client_license( $form );
332 break;
333 }
334 }
335
336 /**
337 * Check license status on schedule
338 */
339 public function check_license_status() {
340 $license = $this->get_license();
341
342 if ( isset( $license['key'] ) && ! empty( $license['key'] ) ) {
343 $response = $this->check( $license['key'] );
344
345 if ( isset( $response['success'] ) && $response['success'] ) {
346 $license['status'] = 'activate';
347 $license['remaining'] = $response['remaining'];
348 $license['activation_limit'] = $response['activation_limit'];
349 $license['expiry_days'] = $response['expiry_days'];
350 $license['title'] = $response['title'];
351 $license['source_id'] = $response['source_identifier'];
352 $license['recurring'] = $response['recurring'];
353 } else {
354 $license['status'] = 'deactivate';
355 $license['expiry_days'] = 0;
356 }
357
358 update_option( $this->option_key, $license, false );
359 }
360 }
361
362 /**
363 * Check this is a valid license
364 */
365 public function is_valid() {
366 if ( null !== $this->is_valid_license ) {
367 return $this->is_valid_license;
368 }
369
370 $license = $this->get_license();
371
372 if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
373 $this->is_valid_license = true;
374 } else {
375 $this->is_valid_license = false;
376 }
377
378 return $this->is_valid_license;
379 }
380
381 /**
382 * Check this is a valid license
383 */
384 public function is_valid_by( $option, $value ) {
385 $license = $this->get_license();
386
387 if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
388 if ( isset( $license[ $option ] ) && $license[ $option ] == $value ) {
389 return true;
390 }
391 }
392
393 return false;
394 }
395
396 /**
397 * Styles for licenses page
398 */
399 private function licenses_style() {
400 ?>
401 <style type="text/css">
402 .appsero-license-section {
403 width: 100%;
404 max-width: 1100px;
405 min-height: 1px;
406 box-sizing: border-box;
407 }
408 .appsero-license-settings {
409 background-color: #fff;
410 box-shadow: 0px 3px 10px rgba(16, 16, 16, 0.05);
411 }
412 .appsero-license-settings * {
413 box-sizing: border-box;
414 }
415 .appsero-license-title {
416 background-color: #F8FAFB;
417 border-bottom: 2px solid #EAEAEA;
418 display: flex;
419 align-items: center;
420 padding: 10px 20px;
421 }
422 .appsero-license-title svg {
423 width: 30px;
424 height: 30px;
425 fill: #0082BF;
426 }
427 .appsero-license-title span {
428 font-size: 17px;
429 color: #444444;
430 margin-left: 10px;
431 }
432 .appsero-license-details {
433 padding: 20px;
434 }
435 .appsero-license-details p {
436 font-size: 15px;
437 margin: 0 0 20px 0;
438 }
439 .license-input-key {
440 position: relative;
441 flex: 0 0 72%;
442 max-width: 72%;
443 }
444 .license-input-key input {
445 background-color: #F9F9F9;
446 padding: 10px 15px 10px 48px;
447 border: 1px solid #E8E5E5;
448 border-radius: 3px;
449 height: 45px;
450 font-size: 16px;
451 color: #71777D;
452 width: 100%;
453 box-shadow: 0 0 0 transparent;
454 }
455 .license-input-key input:focus {
456 outline: 0 none;
457 border: 1px solid #E8E5E5;
458 box-shadow: 0 0 0 transparent;
459 }
460 .license-input-key svg {
461 width: 22px;
462 height: 22px;
463 fill: #0082BF;
464 position: absolute;
465 left: 14px;
466 top: 13px;
467 }
468 .license-input-fields {
469 display: flex;
470 justify-content: space-between;
471 margin-bottom: 30px;
472 max-width: 850px;
473 width: 100%;
474 }
475 .license-input-fields button {
476 color: #fff;
477 font-size: 17px;
478 padding: 8px;
479 height: 46px;
480 background-color: #0082BF;
481 border-radius: 3px;
482 cursor: pointer;
483 flex: 0 0 25%;
484 max-width: 25%;
485 border: 1px solid #0082BF;
486 }
487 .license-input-fields button.deactive-button {
488 background-color: #E40055;
489 border-color: #E40055;
490 }
491 .license-input-fields button:focus {
492 outline: 0 none;
493 }
494 .active-license-info {
495 display: flex;
496 }
497 .single-license-info {
498 min-width: 220px;
499 flex: 0 0 30%;
500 }
501 .single-license-info h3 {
502 font-size: 18px;
503 margin: 0 0 12px 0;
504 }
505 .single-license-info p {
506 margin: 0;
507 color: #00C000;
508 }
509 .single-license-info p.occupied {
510 color: #E40055;
511 }
512 .appsero-license-right-form {
513 margin-left: auto;
514 }
515 .appsero-license-refresh-button {
516 padding: 6px 10px 4px 10px;
517 border: 1px solid #0082BF;
518 border-radius: 3px;
519 margin-left: auto;
520 background-color: #0082BF;
521 color: #fff;
522 cursor: pointer;
523 }
524 .appsero-license-refresh-button .dashicons {
525 color: #fff;
526 margin-left: 0;
527 }
528 </style>
529 <?php
530 }
531
532 /**
533 * Show active license information
534 */
535 private function show_active_license_info( $license ) {
536 ?>
537 <div class="active-license-info">
538 <div class="single-license-info">
539 <h3><?php $this->client->_etrans( 'Activations Remaining' ); ?></h3>
540 <?php if ( empty( $license['activation_limit'] ) ) { ?>
541 <p><?php $this->client->_etrans( 'Unlimited' ); ?></p>
542 <?php } else { ?>
543 <p class="<?php echo $license['remaining'] ? '' : 'occupied'; ?>">
544 <?php printf( $this->client->__trans( '%1$d out of %2$d' ), $license['remaining'], $license['activation_limit'] ); ?>
545 </p>
546 <?php } ?>
547 </div>
548 <div class="single-license-info">
549 <h3><?php $this->client->_etrans( 'Expires in' ); ?></h3>
550 <?php
551 if ( false !== $license['expiry_days'] ) {
552 $occupied = $license['expiry_days'] > 21 ? '' : 'occupied';
553 echo '<p class="' . $occupied . '">' . $license['expiry_days'] . ' days</p>';
554 } else {
555 echo '<p>' . $this->client->__trans( 'Never' ) . '</p>';
556 } ?>
557 </div>
558 </div>
559 <?php
560 }
561
562 /**
563 * Show license settings page notices
564 */
565 private function show_license_page_notices() {
566 if ( ! empty( $this->error ) ) {
567 ?>
568 <div class="notice notice-error is-dismissible appsero-license-section">
569 <p><?php echo $this->error; ?></p>
570 </div>
571 <?php
572 }
573
574 if ( ! empty( $this->success ) ) {
575 ?>
576 <div class="notice notice-success is-dismissible appsero-license-section">
577 <p><?php echo $this->success; ?></p>
578 </div>
579 <?php
580 }
581 echo '<br />';
582 }
583
584 /**
585 * Card header
586 */
587 private function show_license_page_card_header( $license ) {
588 ?>
589 <div class="appsero-license-title">
590 <svg enable-background="new 0 0 299.995 299.995" version="1.1" viewBox="0 0 300 300" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
591 <path d="m150 161.48c-8.613 0-15.598 6.982-15.598 15.598 0 5.776 3.149 10.807 7.817 13.505v17.341h15.562v-17.341c4.668-2.697 7.817-7.729 7.817-13.505 0-8.616-6.984-15.598-15.598-15.598z"/>
592 <path d="m150 85.849c-13.111 0-23.775 10.665-23.775 23.775v25.319h47.548v-25.319c-1e-3 -13.108-10.665-23.775-23.773-23.775z"/>
593 <path d="m150 1e-3c-82.839 0-150 67.158-150 150 0 82.837 67.156 150 150 150s150-67.161 150-150c0-82.839-67.161-150-150-150zm46.09 227.12h-92.173c-9.734 0-17.626-7.892-17.626-17.629v-56.919c0-8.491 6.007-15.582 14.003-17.25v-25.697c0-27.409 22.3-49.711 49.711-49.711 27.409 0 49.709 22.3 49.709 49.711v25.697c7.993 1.673 14 8.759 14 17.25v56.919h2e-3c0 9.736-7.892 17.629-17.626 17.629z"/>
594 </svg>
595 <span><?php echo $this->client->__trans( 'Activate License' ); ?></span>
596
597 <?php if ( $license && $license['key'] ) : ?>
598 <form method="post" class="appsero-license-right-form" action="<?php $this->form_action_url(); ?>" novalidate="novalidate" spellcheck="false">
599 <input type="hidden" name="_action" value="refresh">
600 <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
601 <button type="submit" name="submit" class="appsero-license-refresh-button">
602 <span class="dashicons dashicons-update"></span>
603 <?php echo $this->client->__trans( 'Refresh License' ); ?>
604 </button>
605 </form>
606 <?php endif; ?>
607
608 </div>
609 <?php
610 }
611
612 /**
613 * Active client license
614 */
615 private function active_client_license( $form ) {
616 if ( empty( $form['license_key'] ) ) {
617 $this->error = $this->client->__trans( 'The license key field is required.' );
618
619 return;
620 }
621
622 $license_key = sanitize_text_field( $form['license_key'] );
623 $response = $this->activate( $license_key );
624
625 if ( ! $response['success'] ) {
626 $this->error = $response['error'] ? $response['error'] : $this->client->__trans( 'Unknown error occurred.' );
627
628 return;
629 }
630
631 $data = array(
632 'key' => $license_key,
633 'status' => 'activate',
634 'remaining' => $response['remaining'],
635 'activation_limit' => $response['activation_limit'],
636 'expiry_days' => $response['expiry_days'],
637 'title' => $response['title'],
638 'source_id' => $response['source_identifier'],
639 'recurring' => $response['recurring'],
640 );
641
642 update_option( $this->option_key, $data, false );
643
644 $this->success = $this->client->__trans( 'License activated successfully.' );
645 }
646
647 /**
648 * Deactive client license
649 */
650 private function deactive_client_license( $form ) {
651 $license = $this->get_license();
652
653 if ( empty( $license['key'] ) ) {
654 $this->error = $this->client->__trans( 'License key not found.' );
655
656 return;
657 }
658
659 $response = $this->deactivate( $license['key'] );
660
661 $data = array(
662 'key' => '',
663 'status' => 'deactivate',
664 );
665
666 update_option( $this->option_key, $data, false );
667
668 if ( ! $response['success'] ) {
669 $this->error = $response['error'] ? $response['error'] : $this->client->__trans( 'Unknown error occurred.' );
670
671 return;
672 }
673
674 $this->success = $this->client->__trans( 'License deactivated successfully.' );
675 }
676
677 /**
678 * Refresh Client License
679 */
680 private function refresh_client_license( $form = null ) {
681 $license = $this->get_license();
682
683 if( !$license || ! isset( $license['key'] ) || empty( $license['key'] ) ) {
684 $this->error = $this->client->__trans( "License key not found" );
685 return;
686 }
687
688 $this->check_license_status();
689
690 $this->success = $this->client->__trans( 'License refreshed successfully.' );
691 }
692
693 /**
694 * Add license menu page
695 */
696 private function create_menu_page() {
697 call_user_func(
698 'add_' . 'menu' . '_page',
699 $this->menu_args['page_title'],
700 $this->menu_args['menu_title'],
701 $this->menu_args['capability'],
702 $this->menu_args['menu_slug'],
703 array( $this, 'menu_output' ),
704 $this->menu_args['icon_url'],
705 $this->menu_args['position']
706 );
707 }
708
709 /**
710 * Add submenu page
711 */
712 private function create_submenu_page() {
713 call_user_func(
714 'add_' . 'submenu' . '_page',
715 $this->menu_args['parent_slug'],
716 $this->menu_args['page_title'],
717 $this->menu_args['menu_title'],
718 $this->menu_args['capability'],
719 $this->menu_args['menu_slug'],
720 array( $this, 'menu_output' ),
721 $this->menu_args['position']
722 );
723 }
724
725 /**
726 * Add submenu page
727 */
728 private function create_options_page() {
729 call_user_func(
730 'add_' . 'options' . '_page',
731 $this->menu_args['page_title'],
732 $this->menu_args['menu_title'],
733 $this->menu_args['capability'],
734 $this->menu_args['menu_slug'],
735 array( $this, 'menu_output' ),
736 $this->menu_args['position']
737 );
738 }
739
740 /**
741 * Schedule daily sicense checker event
742 */
743 public function schedule_cron_event() {
744 if ( ! wp_next_scheduled( $this->schedule_hook ) ) {
745 wp_schedule_event( time(), 'daily', $this->schedule_hook );
746
747 wp_schedule_single_event( time() + 20, $this->schedule_hook );
748 }
749 }
750
751 /**
752 * Clear any scheduled hook
753 */
754 public function clear_scheduler() {
755 wp_clear_scheduled_hook( $this->schedule_hook );
756 }
757
758 /**
759 * Enable/Disable schedule
760 */
761 private function run_schedule() {
762 switch ( $this->client->type ) {
763 case 'plugin':
764 register_activation_hook( $this->client->file, array( $this, 'schedule_cron_event' ) );
765 register_deactivation_hook( $this->client->file, array( $this, 'clear_scheduler' ) );
766 break;
767
768 case 'theme':
769 add_action( 'after_switch_theme', array( $this, 'schedule_cron_event' ) );
770 add_action( 'switch_theme', array( $this, 'clear_scheduler' ) );
771 break;
772 }
773 }
774
775 /**
776 * Form action URL
777 */
778 private function form_action_url() {
779 $url = add_query_arg(
780 $_GET,
781 admin_url( basename( $_SERVER['SCRIPT_NAME'] ) )
782 );
783
784 echo apply_filters( 'appsero_client_license_form_action', $url );
785 }
786
787 /**
788 * Get input license key
789 *
790 * @param $action
791 *
792 * @return $license
793 */
794 private function get_input_license_value( $action, $license ) {
795 if ( 'active' == $action ) {
796 return isset( $license['key'] ) ? $license['key'] : '';
797 }
798
799 if ( 'deactive' == $action ) {
800 $key_length = strlen( $license['key'] );
801
802 return str_pad(
803 substr( $license['key'], 0, $key_length / 2 ), $key_length, '*'
804 );
805 }
806
807 return '';
808 }
809 }
810