PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.5
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.5
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / licensing / class-charitable-licenses.php

class-charitable-licenses.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.7.0.5, at includes/licensing/class-charitable-licenses.php

951 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class to assist with the setup of extension licenses.
4 *
5 * @package Charitable/Classes/Charitable_Licenses
6 * @version 1.4.20
7 * @author David Bisset
8 * @copyright Copyright (c) 2022, WP Charitable LLC
9 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
10 */
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Licenses' ) ) :
18
19 /**
20 * Charitable_Licenses
21 *
22 * @since 1.0.0
23 */
24 class Charitable_Licenses {
25
26 /* @var string */
27 const UPDATE_URL = 'https://wpcharitable.com';
28
29 /**
30 * The single instance of this class.
31 *
32 * @var Charitable_Licenses|null
33 */
34 private static $instance = null;
35
36 /**
37 * All the registered products requiring licensing.
38 *
39 * @var array
40 */
41 private $products;
42
43 /**
44 * All the stored licenses.
45 *
46 * @var array
47 */
48 private $licenses;
49
50 /**
51 * Cached update data.
52 *
53 * @var array
54 */
55 private $update_data;
56
57 /**
58 * Returns and/or create the single instance of this class.
59 *
60 * @since 1.2.0
61 *
62 * @return Charitable_Licenses
63 */
64 public static function get_instance() {
65 if ( is_null( self::$instance ) ) {
66 self::$instance = new self();
67 }
68
69 return self::$instance;
70 }
71
72 /**
73 * Create class object.
74 *
75 * @since 1.0.0
76 */
77 private function __construct() {
78 $this->products = array();
79 $this->update_data = array();
80
81 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_updates' ) );
82 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
83 add_action( 'charitable_deactivate_license', array( $this, 'deactivate_license' ) );
84 add_filter( 'upgrader_pre_download', array( $this, 'set_upgrader_error_message' ), 10, 3 );
85 add_filter( 'upgrader_package_options', array( $this, 'set_upgrader_package_options' ) );
86
87 add_action( 'init', array( $this, 'ajax_license_check' ) );
88 add_action( 'wp_ajax_charitable_license_check', array( $this, 'ajax_license_check' ) );
89 }
90
91 /**
92 * Checks licenses (new and legacy) to see if any are valid.
93 *
94 * @since 1.7.0
95 *
96 * @return boolean
97 */
98 public function is_pro() {
99
100 $products = $this->get_products();
101
102 if ( is_multisite() ) {
103
104 // get the main blog id to check for license
105 $network = get_network();
106 if ( ! $network ) {
107 return 0;
108 }
109
110 $blog_id = $network->site_id;
111
112 switch_to_blog( $blog_id );
113
114 if ( $this->is_v3_license_valid() ) {
115 return true;
116 }
117
118 $licenses = charitable_get_option( 'licenses', array() );
119
120 // go through licenses - if there is at least ONE VALID ADDON LICNESE then it's pro.
121 foreach ( $licenses as $slug => $license ) {
122 if ( isset( $license['valid'] ) && 1 === intval( $license['valid'] ) ) {
123 restore_current_blog();
124 return true;
125 }
126 }
127
128 restore_current_blog();
129
130 } else {
131
132 if ( empty( $products ) ) {
133 return false;
134 }
135
136 if ( $this->is_v3_license_valid() ) {
137 return true;
138 }
139
140 // fallback: go through licenses - if there is at least ONE VALID ADDON LICNESE then it's pro.
141 foreach ( $products as $slug => $item ) {
142 $valid = $this->has_valid_license( $slug );
143 if ( $valid ) {
144 return true;
145 }
146 }
147 }
148 }
149
150 /**
151 * Checks the "v3" license (starting Nov 2022) to see if it's valid.
152 * If valid, normally doesn't require other license/pro checks.
153 *
154 * @since 1.7.0.4
155 *
156 * @return boolean
157 */
158 public function is_v3_license_valid() {
159
160 $settings = get_option( 'charitable_settings' );
161 if ( false === $settings || ! isset( $settings['licenses']['charitable-v2'] ) || ! isset( $settings['licenses']['charitable-v2'][ 'valid' ] ) ) {
162 return false;
163 }
164 $valid = $settings['licenses']['charitable-v2'][ 'valid' ];
165 if ( 1 === intval( $valid ) ) {
166 return true;
167 }
168
169 return false;
170
171 }
172
173 /**
174 * Checks for any Charitable extensions with updates.
175 *
176 * @since 1.4.0
177 *
178 * @param array $_transient_data The plugin updates data.
179 * @return array
180 */
181 public function check_for_updates( $_transient_data ) {
182 global $pagenow;
183
184 if ( ! is_object( $_transient_data ) ) {
185 $_transient_data = new stdClass;
186 }
187
188 if ( 'plugins.php' == $pagenow && is_multisite() ) {
189 return $_transient_data;
190 }
191
192 /* Loop over our licensed products and check whether any are missing transient data. */
193 $missing_data = array();
194
195 foreach ( $this->get_products() as $product ) {
196 if ( $this->is_missing_version_info( $product, $_transient_data ) ) {
197 $missing_data[] = $product;
198 }
199 }
200
201 /* If we are missing data for any of our products, check whether any have an update. */
202 if ( ! empty( $missing_data ) ) {
203
204 $versions = $this->get_versions();
205
206 unset( $versions['request_speed'] );
207
208 if ( ! empty( $versions ) ) {
209
210 $versions_name_lookup = wp_list_pluck( $versions, 'name' );
211
212 foreach ( $missing_data as $product ) {
213
214 if ( ! in_array( $product['name'], $versions_name_lookup ) ) {
215 continue;
216 }
217
218 $plugin_file = plugin_basename( $product['file'] );
219 $product_key = array_search( $product['name'], wp_list_pluck( $this->get_products(), 'name' ) );
220 $version_info = $versions[ array_search( $product['name'], $versions_name_lookup ) ];
221 $version_info['license'] = $this->get_license_details( $product_key );
222
223 if ( version_compare( $product['version'], $version_info['new_version'], '<' ) ) {
224
225 if ( isset( $version_info['sections'] ) ) {
226 $version_info['sections'] = maybe_unserialize( $version_info['sections'] );
227 }
228
229 $can_update = $this->able_to_update( $version_info );
230
231 if ( is_array( $can_update ) ) {
232 $version_info['package'] = $can_update['reason_code'];
233 $version_info['download_link'] = $can_update['reason_code'];
234 $version_info['package_download_restriction'] = $can_update['description'];
235 }
236
237 $_transient_data->response[ $plugin_file ] = (object) $version_info;
238 }
239
240 $_transient_data->last_checked = time();
241 $_transient_data->checked[ $plugin_file ] = $product['version'];
242
243 }//end foreach
244 }//end if
245 }//end if
246
247 return $_transient_data;
248 }
249
250 /**
251 * Updates information on the "View version x.x details" page with custom data.
252 *
253 * @uses api_request()
254 *
255 * @since 1.4.20
256 *
257 * @param mixed $_data Default set of data.
258 * @param string $_action The current action.
259 * @param object $_args Request args.
260 * @return object $_data
261 */
262 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
263 if ( 'plugin_information' != $_action ) {
264 return $_data;
265 }
266
267 if ( ! isset( $_args->slug ) ) {
268 return $_data;
269 }
270
271 $plugin_key = str_replace( '-', '_', $_args->slug );
272
273 if ( ! array_key_exists( $plugin_key, $this->products ) ) {
274 return $_data;
275 }
276
277 $version_info = $this->get_version_info( plugin_basename( $this->products[ $plugin_key ]['file'] ) );
278
279 if ( $version_info ) {
280 $_data = $version_info;
281 }
282
283 return $_data;
284 }
285
286 /**
287 * Return whether a particular plugin is missing version info.
288 *
289 * @since 1.4.20
290 *
291 * @param array $product Product details array.
292 * @param false|object $update_cache Optional argument to pass update cache.
293 * @return boolean
294 */
295 public function is_missing_version_info( $product, $update_cache = false ) {
296 return ! $this->get_version_info( plugin_basename( $product['file'] ), $update_cache );
297 }
298
299 /**
300 * Return the version update info for a particular plugin.
301 *
302 * @since 1.4.20
303 *
304 * @param string $slug The plugin slug.
305 * @param false|object $update_cache Optional argument to pass update cache.
306 * @return array|false Array if an update is available. False otherwise.
307 */
308 public function get_version_info( $slug, $update_cache = false ) {
309 if ( ! $update_cache ) {
310 $update_cache = get_site_transient( 'update_plugins' );
311 }
312
313 if ( ! is_object( $update_cache ) || empty( $update_cache->response ) || ! array_key_exists( $slug, $update_cache->response ) ) {
314 return false;
315 }
316
317 return $update_cache->response[ $slug ];
318 }
319
320 /**
321 * Display an error message when users attempt to update a plugin
322 * without a license or with an expired license.
323 *
324 * @since 1.4.20
325 *
326 * @param false|WP_Error $reply The messaage to return. Set to false by default.
327 * @param string $package Package URL. For Charitable extensions without a
328 * license or that have expired, we set this to a
329 * key. Kind of a hack to pass a message to this hook.
330 * @param Plugin_Upgrader $upgrader The Plugin_Upgrader object.
331 * @return false|WP_Error
332 */
333 public function set_upgrader_error_message( $reply, $package, $upgrader ) {
334 $ajax_skin = 'WP_Ajax_Upgrader_Skin' == get_class( $upgrader->skin );
335
336 if ( 'missing_license' == $package ) {
337 if ( $ajax_skin ) {
338 $message = sprintf( __( 'You have not activated your license key. Activate your license to update: %s', 'charitable' ),
339 admin_url( 'admin.php?page=charitable-settings&tab=licenses' )
340 );
341 } else {
342 $message = sprintf( __( 'You have not activated your license key. <a href="%s" target="_top">Activate your license to update.</a>', 'charitable' ),
343 admin_url( 'admin.php?page=charitable-settings&tab=licenses' )
344 );
345 }
346
347 return new WP_Error( 'missing_license_key', $message );
348 }
349
350 if ( false !== strpos( $package, 'expired_license:' ) ) {
351
352 $renewal_link = str_replace( 'expired_license:', '', $package );
353
354 if ( $ajax_skin ) {
355 $message = sprintf( __( 'Your license has expired. Renew your license key: %s', 'charitable' ),
356 $renewal_link
357 );
358 } else {
359 $message = sprintf( __( 'Your license has expired. <a href="%s" target="_blank">Renew your license key.</a>', 'charitable' ),
360 esc_url( $renewal_link )
361 );
362 }
363
364 return new WP_Error( 'expired_license_key', $message );
365 }
366
367 if ( false !== strpos( $package, 'missing_requirements:' ) ) {
368 $message = str_replace( 'missing_requirements:', '', htmlspecialchars_decode( $package ) );
369
370 return new WP_Error( 'missing_requirements_key', $message );
371 }
372
373 return $reply;
374 }
375
376 /**
377 * Checks whether the given plugin can be updated.
378 *
379 * @since 1.6.14
380 *
381 * @param array $version_info Version information.
382 * @return true|array If it can be updated, returns true. Otherwise
383 * returns an array with a reason_code and description.
384 */
385 protected function able_to_update( $version_info ) {
386 $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $version_info['name'] . '&slug=' . $version_info['slug'] . '&TB_iframe=true&width=772&height=911' );
387
388 switch ( $version_info['package'] ) {
389
390 case 'missing_license':
391 return array(
392 'reason_code' => 'missing_license',
393 'description' => sprintf(
394 __( '<p>There is a new version of %1$s available but you have not activated your license. <a target="_top" href="%2$s">Activate your license</a> or <a target="_blank" class="thickbox" href="%3$s">view version %4$s details</a>.</p>', 'charitable' ),
395 esc_html( $version_info['name'] ),
396 admin_url( 'admin.php?page=charitable-settings&tab=licenses' ),
397 esc_url( $changelog_link ),
398 esc_html( $version_info['new_version'] )
399 ),
400 );
401
402 case 'expired_license':
403 $base_renewal_url = isset( $version_info['renewal_link'] ) ? $version_info['renewal_link'] : 'https://www.wpcharitable.com/account';
404
405 return array(
406 'reason_code' => 'expired_license',
407 'description' => sprintf(
408 __( '<p>There is a new version of %1$s available but your license has expired. <a target="_blank" href="%2$s">Renew your license</a> or <a target="_blank" class="thickbox" href="%3$s">view version %4$s details</a>.</p>', 'charitable' ),
409 esc_html( $version_info['name'] ),
410 charitable_ga_url(
411 $base_renewal_url,
412 urlencode( 'Expired License Notice'),
413 urlencode( 'Renew your license' )
414 ),
415 esc_url( $changelog_link ),
416 esc_html( $version_info['new_version'] )
417 ),
418 );
419
420 default:
421 if ( ! isset( $version_info['requirements'] ) ) {
422 return true;
423 }
424
425 $messages = array();
426
427 foreach ( $version_info['requirements'] as $type => $details ) {
428
429 switch ( $type ) {
430 case 'php':
431 if ( version_compare( phpversion(), $details, '<' ) ) {
432 $messages[] = esc_html(
433 sprintf(
434 __( '<li>Requires PHP version %s or greater.</li>' ),
435 $details
436 )
437 );
438 }
439 break;
440
441 case 'charitable':
442 if ( version_compare( charitable()->get_version(), $details, '<' ) ) {
443 $messages[] = esc_html(
444 sprintf(
445 __( 'Requires Charitable version %s or greater.' ),
446 $details
447 )
448 );
449 }
450 break;
451 }
452
453 if ( empty( $messages ) ) {
454 return true;
455 }
456
457 return array(
458 'reason_code' => 'missing_requirements',
459 'description' => sprintf(
460 __( '<p>There is a new version of %1$s available but you are missing the following minimum requirements:</p>%2$s', 'charitable' ),
461 esc_html( $version_info['name'] ),
462 '<ul>' . implode( '<br/>', $messages ) . '</ul>'
463 ),
464 );
465 }
466
467 return true;
468 }
469 }
470
471 /**
472 * Set upgrader package options.
473 *
474 * This is used for expired licenses and in certain cases for products that do
475 * not have a license activated.
476 *
477 * @since 1.4.20
478 *
479 * @param array $options Upgrader package options.
480 * @return array
481 */
482 public function set_upgrader_package_options( $options ) {
483 if ( 'expired_license' != $options['package'] && empty( $options['package'] ) ) {
484 return $options;
485 }
486
487 if ( ! array_key_exists( 'hook_extra', $options ) || ! array_key_exists( 'plugin', $options['hook_extra'] ) ) {
488 return $options;
489 }
490
491 list( $plugin_key, ) = explode( '/', str_replace( '-', '_', $options['hook_extra']['plugin'] ) );
492
493 if ( ! array_key_exists( $plugin_key, $this->products ) ) {
494 return $options;
495 }
496
497 /* Set up the renewal link for expired licenses. */
498 if ( 'expired_license' == $options['package'] ) {
499 $options['package'] = $this->get_expired_license_package( $options['hook_extra']['plugin'] );
500
501 return $options;
502 }
503
504 /* Set up the renewal link for plugins where minimum requirements haven't been met. */
505 if ( 'missing_requirements' == $options['package'] ) {
506 $options['package'] = $this->get_missing_requirements_package( $options['hook_extra']['plugin'] );
507
508 return $options;
509 }
510
511 $license_details = $this->get_license_details( $plugin_key );
512
513 if ( ! is_array( $license_details ) || ! array_key_exists( 'license', $license_details ) || empty( $license_details['license'] ) ) {
514 $options['package'] = 'missing_license';
515 }
516
517 return $options;
518 }
519
520 /**
521 * Return the package string for an expired license.
522 *
523 * @since 1.4.20
524 *
525 * @param string $plugin Plugin basename.
526 * @return string
527 */
528 public function get_expired_license_package( $plugin ) {
529 $version_info = $this->get_version_info( $plugin );
530 $base_renewal_url = isset( $version_info->renewal_link ) ? $version_info->renewal_link : 'https://www.wpcharitable.com/account';
531
532 return sprintf( 'expired_license:%s', charitable_ga_url(
533 $base_renewal_url,
534 urlencode( 'WordPress Dashboard'),
535 urlencode( 'Expired License' )
536 ));
537
538 }
539
540 /**
541 * Return the package string for a plugin missing requirements.
542 *
543 * @since 1.6.14
544 *
545 * @param string $plugin Plugin basename.
546 * @return string
547 */
548 public function get_missing_requirements_package( $plugin ) {
549 $version_info = $this->get_version_info( $plugin );
550
551 return sprintf(
552 'missing_requirements:%s',
553 $version_info->package_download_restriction
554 );
555 }
556
557 /**
558 * Register a product that requires licensing.
559 *
560 * @since 1.0.0
561 *
562 * @param string $item_name The title of the product.
563 * @param string $author The author of the product.
564 * @param string $version The current product version we have installed.
565 * @param string $file The path to the plugin file.
566 * @param string $url The base URL where the plugin is licensed. Defaults to Charitable_Licenses::UPDATE_URL.
567 * @return void
568 */
569 public function register_licensed_product( $item_name, $author, $version, $file, $url = false ) {
570 if ( ! $url ) {
571 $url = Charitable_Licenses::UPDATE_URL;
572 }
573
574 $product_key = $this->get_item_key( $item_name );
575
576 $this->products[ $product_key ] = array(
577 'name' => $item_name,
578 'author' => $author,
579 'version' => $version,
580 'url' => $url,
581 'file' => $file,
582 );
583
584 $licenses = $this->get_licenses();
585 $license = isset( $licenses[ $product_key ]['license'] ) ? trim( $licenses[ $product_key ]['license'] ) : '';
586
587 new Charitable_Plugin_Updater( $url, $file, array(
588 'version' => $version,
589 'license' => $license,
590 'item_name' => $item_name,
591 'author' => $author,
592 ) );
593
594 }
595
596 /**
597 * Return the list of products requiring licensing.
598 *
599 * @since 1.0.0
600 *
601 * @return array[]
602 */
603 public function get_products() {
604 return $this->products;
605 }
606
607 /**
608 * Return a specific product's details.
609 *
610 * @since 1.0.0
611 *
612 * @param string $item The item for which we are getting product details.
613 * @return string[]
614 */
615 public function get_product_license_details( $item ) {
616 return isset( $this->products[ $item ] ) ? $this->products[ $item ] : false;
617 }
618
619 /**
620 * Returns whether the given product has a valid license.
621 *
622 * @since 1.0.0
623 *
624 * @param string $item The item to check.
625 * @return boolean
626 */
627 public function has_valid_license( $item ) {
628 $license = $this->get_license_details( $item );
629
630 if ( ! $license || ! isset( $license['valid'] ) ) {
631 return false;
632 }
633
634 return $license['valid'];
635 }
636
637 /**
638 * Returns the license details for the given product.
639 *
640 * @since 1.0.0
641 *
642 * @param string $item The item to get the license for.
643 * @return mixed[]
644 */
645 public function get_license( $item ) {
646 $license = $this->get_license_details( $item );
647
648 if ( ! $license || ! is_array( $license ) ) {
649 return false;
650 }
651
652 return $license['license'];
653 }
654
655 /**
656 * Returns the active license details for the given product.
657 *
658 * @since 1.0.0
659 *
660 * @param string $item The item to get active licensing details for.
661 * @return mixed[]
662 */
663 public function get_license_details( $item ) {
664 $licenses = $this->get_licenses();
665
666 if ( ! isset( $licenses[ $item ] ) ) {
667 return false;
668 }
669
670 return $licenses[ $item ];
671 }
672
673 /**
674 * Return the list of licenses.
675 *
676 * Note: The licenses are not necessarily valid. If a user enters an invalid
677 * license, the license will be stored but it will be flagged as invalid.
678 *
679 * @since 1.0.0
680 *
681 * @return array[]
682 */
683 public function get_licenses() {
684 if ( ! isset( $this->licenses ) ) {
685 $this->licenses = charitable_get_option( 'licenses', array() );
686 }
687
688 return $this->licenses;
689 }
690
691 /**
692 * Verify a license.
693 *
694 * @since 1.0.0
695 *
696 * @param string $item The item to verify.
697 * @param string $license The license key for the item.
698 * @param boolean $force Whether to force the verification check.
699 * @return mixed[]
700 */
701 public function verify_license( $item, $license, $force = false, $legacy = false ) {
702
703 $license = trim( $license );
704
705 if ( $license === $this->get_license( $item ) && ! $force ) {
706 return $this->get_license_details( $item );
707 }
708
709 $product_details = $this->get_product_license_details( $item );
710
711 /* This product was not correctly registered. */
712 if ( ! $product_details ) {
713 return;
714 }
715
716 /* Data to send in our API request */
717 $api_params = array(
718 'edd_action' => 'activate_license',
719 'license' => $license,
720 'legacy' => $legacy,
721 'item_name' => urlencode( $product_details['name'] ),
722 'url' => home_url(),
723 );
724
725 /* Call the custom API */
726 $response = wp_remote_post(
727 $product_details['url'],
728 array(
729 'timeout' => 15,
730 'sslverify' => false,
731 'body' => $api_params,
732 )
733 );
734
735 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
736 error_log( print_r( $api_params, true ) );
737 error_log( print_r( $product_details, true ) );
738 error_log( print_r( $response, true ) );
739 }
740
741 /* Make sure the response came back okay */
742 if ( is_wp_error( $response ) ) {
743 return;
744 }
745
746 $this->flush_update_cache();
747
748 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
749
750 $comm_success = isset( $license_data ) && ! empty( $license_data ) ? true : false;
751
752 // if the license is not valid, attempt to discover why - in this case, maybe the activation limit was exceeded.
753
754 $license_limit = isset( $license_data->license_limit ) ? intval( $license_data->license_limit ) : false;
755 $site_count = isset( $license_data->site_count ) ? intval( $license_data->site_count ) : false;
756 $limit_exceeded = ( $license_limit > 0 && $site_count >= $license_limit ) ? true : false;
757
758 return array(
759 'license' => $license,
760 'expiration_date' => isset( $license_data->expires) ? $license_data->expires : false,
761 'plan_id' => isset( $license_data->price_id ) ? $license_data->price_id : false,
762 'valid' => ( isset( $license_data->license ) && 'valid' === $license_data->license ),
763 'license_limit' => $limit_exceeded,
764 'comm_success' => $comm_success,
765 );
766 }
767
768 /**
769 * Return the URL to deactivate a specific license.
770 *
771 * @since 1.0.0
772 *
773 * @param string $item The item to deactivate.
774 * @return string
775 */
776 public function get_license_deactivation_url( $item ) {
777 return esc_url( add_query_arg( array(
778 'charitable_action' => 'deactivate_license',
779 'product_key' => $item,
780 '_nonce' => wp_create_nonce( 'license' ),
781 ), admin_url( 'admin.php?page=charitable-settings&tab=advanced' ) ) );
782 }
783
784 /**
785 * Deactivate a license.
786 *
787 * @since 1.0.0
788 *
789 * @return void
790 */
791 public function deactivate_license() {
792 if ( ! wp_verify_nonce( $_REQUEST['_nonce'], 'license' ) ) {
793 wp_die( esc_attr__( 'Cheatin\' eh?!', 'charitable' ) );
794 }
795
796 $product_key = isset( $_REQUEST['product_key'] ) ? $_REQUEST['product_key'] : false;
797
798 /* Product key must be set */
799 if ( false === $product_key ) {
800 wp_die( esc_attr__( 'Missing product key', 'charitable' ) );
801 }
802
803 $product = $this->get_product_license_details( $product_key );
804
805 /* Make sure we have a valid product with a valid license. */
806 if ( ! $product || ! $this->has_valid_license( $product_key ) ) {
807 wp_die( esc_attr__( 'This product is not valid or does not have a valid license key.', 'charitable' ) );
808 }
809
810 $license = $this->get_license( $product_key );
811
812 /* Data to send to wpcharitable.com to deactivate the license. */
813 $api_params = array(
814 'edd_action' => 'deactivate_license',
815 'license' => $license,
816 'item_name' => urlencode( $product['name'] ),
817 'url' => home_url(),
818 );
819
820 /* Call the custom API. */
821 $response = wp_remote_post( $product['url'], array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
822
823 /* Make sure the response came back okay */
824 if ( is_wp_error( $response ) ) {
825 return;
826 }
827
828 $this->flush_update_cache();
829
830 /* Decode the license data */
831 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
832
833 $settings = get_option( 'charitable_settings' );
834
835 unset( $settings['licenses'][ $product_key ] );
836
837 update_option( 'charitable_settings', $settings );
838
839 wp_redirect( add_query_arg( array( 'tab' => 'advanced' ), admin_url( 'admin.php?page=charitable-settings' ) ) );
840 exit;
841
842 }
843
844 /**
845 * Flush the version update cache.
846 *
847 * @since 1.4.20
848 *
849 * @return void
850 */
851 protected function flush_update_cache() {
852 wp_cache_delete( 'plugin_versions', 'charitable' );
853 set_site_transient( 'update_plugins', null );
854 }
855
856 /**
857 * Return a key for the item, based on the item name.
858 *
859 * @since 1.0.0
860 *
861 * @param string $item_name Name of the item.
862 * @return string
863 */
864 protected function get_item_key( $item_name ) {
865 return strtolower( str_replace( ' ', '_', $item_name ) );
866 }
867
868 /**
869 * Return the latest versions of Charitable plugins.
870 *
871 * @since 1.4.0
872 *
873 * @return array
874 */
875 protected function get_versions() {
876 $versions = wp_cache_get( 'plugin_versions', 'charitable' );
877
878 if ( false === $versions ) {
879
880 $licenses = array();
881
882 foreach ( $this->get_licenses() as $license ) {
883 if ( isset( $license['license'] ) ) {
884 $licenses[] = $license['license'];
885 }
886 }
887
888 $response = wp_remote_post(
889 Charitable_Licenses::UPDATE_URL . '/edd-api/versions-v2/',
890 array(
891 'sslverify' => false,
892 'timeout' => 15,
893 'body' => array(
894 'licenses' => $licenses,
895 'url' => home_url(),
896 ),
897 )
898 );
899
900 $versions = wp_remote_retrieve_body( $response );
901
902 $versions = json_decode( $versions, true );
903
904 wp_cache_set( 'plugin_versions', $versions, 'charitable' );
905 } //end if
906
907 return $versions;
908 }
909
910 public function ajax_license_check() {
911
912 if ( ! is_admin() ) {
913 return;
914 }
915
916 if ( ! isset( $_REQUEST['license'] ) ) {
917 return;
918 }
919
920 $product_key = 'charitable';
921 $re_check = true;
922 $license = trim( esc_html( $_REQUEST['license'] ) );
923 $license_data = $this->verify_license( $product_key, $license, $re_check );
924
925 $this->flush_update_cache();
926
927 $settings = get_option( 'charitable_settings' );
928
929 $settings['licenses'][ $product_key . '-v2' ] = array(
930 'license' => $license,
931 'expiration_date' => isset( $license_data['expiration_date'] ) ? $license_data['expiration_date'] : false,
932 'plan_id' => isset( $license_data['plan_id'] ) ? $license_data['plan_id'] : false,
933 'valid' => isset( $license_data['plan_id'] ) ? 1 === intval( $license_data['valid'] ) : false,
934 );
935
936 update_option( 'charitable_settings', $settings );
937
938 if ( isset( $license_data['valid'] ) && intval( $license_data['valid'] ) === 1 ) {
939 $license_data['message'] = Charitable_Licenses_Settings::get_instance()->get_licensed_message();
940 } else {
941 $license_data['message'] = Charitable_Licenses_Settings::get_instance()->get_unlicensed_message( false, $license_data );
942 }
943
944 wp_send_json_success( $license_data );
945
946 }
947
948 }
949
950 endif;
951