PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.12
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.12
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 / admin / addons-directory / class-charitable-addons-directory.php

class-charitable-addons-directory.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.7.0.12, at includes/admin/addons-directory/class-charitable-addons-directory.php

1,053 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Addons Directory.
4 *
5 * @package Charitable/Classes/Charitable_Addons_Directory
6 * @author David Bisset
7 * @copyright Copyright (c) 2022, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 * @version 1.0.0
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Addons_Directory' ) ) :
19
20 /**
21 * Charitable_Addons_Directory
22 *
23 * @final
24 * @since 1.0.0
25 */
26 final class Charitable_Addons_Directory {
27
28 /* @var string */
29 const UPDATE_URL = 'https://wpcharitable.com';
30
31 /**
32 * The single instance of this class.
33 *
34 * @var Charitable_Addons_Directory|null
35 */
36 private static $instance = null;
37
38 /**
39 * All the stored licenses.
40 *
41 * @var array
42 */
43 private $licenses;
44
45 /**
46 * Determine if the plugin/addon installations are allowed.
47 *
48 * @since 1.6.7
49 *
50 * @var bool
51 */
52 private $can_install;
53
54 /**
55 * Create object instance.
56 *
57 * @since 1.0.0
58 */
59 private function __construct() {
60
61 // Maybe load addons page.
62 add_action( 'admin_init', array ( $this, 'init' ) );
63
64 do_action( 'charitable_addons_directory_settings_start', $this );
65
66 add_action( 'admin_enqueue_scripts', array( $this, 'charitable_admin_scripts' ) );
67
68 // Add callbacks for content.
69 add_action( 'charitable_addons_directory_section', array( $this, 'addons_directory_content' ) );
70
71 }
72
73 /**
74 * Init.
75 *
76 * @since 1.6.7
77 */
78 public function init() {
79
80 $this->can_install = charitable_can_install( 'addon' );
81
82 }
83
84 public function charitable_admin_scripts() {
85
86 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
87 $suffix = '';
88 $version = '';
89 } else {
90 $suffix = '.min';
91 $version = charitable()->get_version();
92 }
93
94 $assets_dir = charitable()->get_path( 'assets', false );
95
96 $strings = [
97 'addon_activate' => esc_html__( 'Activate', 'charitable' ),
98 'addon_activated' => esc_html__( 'Activated', 'charitable' ),
99 'addon_active' => esc_html__( 'Active', 'charitable' ),
100 'addon_deactivate' => esc_html__( 'Deactivate', 'charitable' ),
101 'addon_inactive' => esc_html__( 'Inactive', 'charitable' ),
102 'addon_install' => esc_html__( 'Install Addon', 'charitable' ),
103 'addon_error' => sprintf(
104 wp_kses( /* translators: %1$s - An addon download URL, %2$s - Link to manual installation guide. */
105 __( 'Could not install the addon. Please <a href="%1$s" target="_blank" rel="noopener noreferrer">download it from wpcharitable.com</a> and <a href="%2$s" target="_blank" rel="noopener noreferrer">install it manually</a>.', 'charitable' ),
106 [
107 'a' => [
108 'href' => true,
109 'target' => true,
110 'rel' => true,
111 ],
112 ]
113 ),
114 'https://wpcharitable.com/account/licenses/',
115 'https://wpcharitable.com/docs/how-to-manually-install-addons-in-charitable/'
116 ),
117 'plugin_error' => esc_html__( 'Could not install the plugin automatically. Please download and install it manually.', 'charitable' ),
118 'addon_search' => esc_html__( 'Searching Addons', 'charitable' ),
119 'ajax_url' => admin_url( 'admin-ajax.php' ),
120 'admin_url' => admin_url(),
121 'cancel' => esc_html__( 'Cancel', 'charitable' ),
122 'close' => esc_html__( 'Close', 'charitable' ),
123 'plugin_install_activate_btn' => esc_html__( 'Install and Activate', 'charitable' ),
124 'plugin_install_activate_confirm' => esc_html__( 'needs to be installed and activated to import its forms. Would you like us to install and activate it for you?', 'charitable' ),
125 'plugin_activate_btn' => esc_html__( 'Activate', 'charitable' ),
126 'plugin_activate_confirm' => esc_html__( 'needs to be activated to import its forms. Would you like us to activate it for you?', 'charitable' ),
127 'connecting' => esc_html__( 'Connecting...', 'charitable' ),
128 'save_refresh' => esc_html__( 'Save and Refresh', 'charitable' ),
129 'server_error' => esc_html__( 'Unfortunately there was a server connection error.', 'charitable' ),
130 'testing' => esc_html__( 'Testing', 'charitable' ),
131 'upgrade_completed' => esc_html__( 'Upgrade was successfully completed!', 'charitable' ),
132 'edit_license' => esc_html__( 'To edit the License Key, please first click the Deactivate Key button. Please note that deactivating this key will remove access to updates, addons, and support.', 'charitable' ),
133 'something_went_wrong' => esc_html__( 'Something went wrong', 'charitable' ),
134 'success' => esc_html__( 'Success', 'charitable' ),
135 'loading' => esc_html__( 'Loading...', 'charitable' ),
136 'use_simple_contact_form' => esc_html__( 'Use Simple Contact Form Template', 'charitable' ),
137 'error_select_template' => esc_html__( 'Something went wrong while applying the template.', 'charitable' ),
138 'nonce' => wp_create_nonce( 'charitable-admin' ),
139 ];
140
141
142 wp_enqueue_script(
143 'listjs',
144 $assets_dir . 'js/libraries/list.min.js',
145 [ 'jquery' ],
146 $version,
147 false
148 );
149
150 wp_register_style(
151 'charitable-admin-addons-directory',
152 $assets_dir . 'css/charitable-admin-addons-directory' . $suffix . '.css',
153 array(),
154 $version
155 );
156 wp_enqueue_style( 'charitable-admin-addons-directory' );
157
158 wp_register_script(
159 'charitable-admin-addon-directory',
160 $assets_dir . 'js/charitable-admin-addon-directory' . $suffix . '.js',
161 array( 'jquery' ),
162 $version,
163 true
164 );
165
166 wp_enqueue_script( 'charitable-admin-addon-directory' );
167
168 wp_localize_script(
169 'charitable-admin-addon-directory',
170 'charitable_admin',
171 $strings
172 );
173
174 }
175
176 /**
177 * Returns and/or create the single instance of this class.
178 *
179 * @since 1.2.0
180 *
181 * @return Charitable_Addons_Directory
182 */
183 public static function get_instance() {
184 if ( is_null( self::$instance ) ) {
185 self::$instance = new self();
186 }
187
188 return self::$instance;
189 }
190
191 /**
192 * Attemps to confirm if the current license is a legacy one, even if we have a plan_id / slug (which normally came from newer license)
193 *
194 * @since 1.7.0.4
195 *
196 * @return Charitable_Addons_Directory
197 */
198 public static function is_current_plan_legacy() {
199
200 $settings = get_option( 'charitable_settings' );
201
202 // always check the v2 settings first.
203 $plan_id = isset( $settings['licenses']['charitable-v2'][ 'plan_id' ] ) ? intval( $settings['licenses']['charitable-v2'][ 'plan_id' ] ) : false;
204 $valid = isset( $settings['licenses']['charitable-v2'][ 'valid' ] ) ? trim( esc_html( $settings['licenses']['charitable-v2'][ 'valid' ] ) ) : false;
205
206 if ( false === $valid || '' === $valid ) {
207 // try looking at the "legacy" license for a plan_id
208 if ( isset( $settings['licenses']['charitable'] ) ) {
209 $plan_id = isset( $settings['licenses']['charitable'][ 'plan_id' ] ) ? intval( $settings['licenses']['charitable'][ 'plan_id' ] ) : false;
210 $valid = isset( $settings['licenses']['charitable'][ 'valid' ] ) ? trim( esc_html( $settings['licenses']['charitable'][ 'valid' ] ) ) : false;
211
212 if ( $plan_id ) {
213 return true;
214 }
215 }
216
217 // try looking for an install that just installed an addon license only, which would be another example of a "legacy" license
218 if ( ! empty( $settings['licenses'] ) ) {
219 $addon_licenses = $settings['licenses'];
220 if ( isset( $addon_licenses['charitable'] ) ) {
221 unset( $addon_licenses['charitable'] );
222 }
223 if ( isset( $addon_licenses['charitable_pro'] ) ) {
224 unset( $addon_licenses['charitable_pro'] );
225 }
226 foreach ( $addon_licenses as $addon_license ) {
227 if ( isset( $addon_license['valid'] ) && 1 === intval( $addon_license['valid'] ) ) {
228 return true;
229 }
230 }
231 }
232
233 }
234
235 return false;
236
237 }
238
239 /**
240 * Returns and/or create the single instance of this class.
241 *
242 * @since 1.2.0
243 *
244 * @return Charitable_Addons_Directory
245 */
246 public static function get_current_plan_slug() {
247
248 $settings = get_option( 'charitable_settings' );
249
250 // always check the v2 settings first.
251 $plan_id = isset( $settings['licenses']['charitable-v2'][ 'plan_id' ] ) ? intval( $settings['licenses']['charitable-v2'][ 'plan_id' ] ) : false;
252 $valid = isset( $settings['licenses']['charitable-v2'][ 'valid' ] ) ? trim( esc_html( $settings['licenses']['charitable-v2'][ 'valid' ] ) ) : false;
253
254 if ( false === $valid || '' === $valid ) {
255 // try looking at the "legacy" license for a plan_id
256 if ( isset( $settings['licenses']['charitable'] ) ) {
257 $plan_id = isset( $settings['licenses']['charitable'][ 'plan_id' ] ) ? intval( $settings['licenses']['charitable'][ 'plan_id' ] ) : false;
258 $valid = isset( $settings['licenses']['charitable'][ 'valid' ] ) ? trim( esc_html( $settings['licenses']['charitable'][ 'valid' ] ) ) : false;
259 }
260 }
261
262 if ( $valid ) {
263 switch ( $plan_id ) {
264 case 1:
265 $plan_slug = 'basic';
266 break;
267 case 2:
268 $plan_slug = 'plus';
269 break;
270 case 3:
271 $plan_slug = 'pro';
272 break;
273 case 4:
274 $plan_slug = 'agency';
275 break;
276 default:
277 $plan_slug = 'lite';
278 break;
279 }
280 } else {
281 $plan_slug = 'lite';
282 }
283
284 return $plan_slug;
285
286 }
287
288 /**
289 * Callback for displaying the UI for Addons.
290 *
291 * @since 1.7.0
292 */
293 public function addons_directory_content() {
294
295 // Get Addons.
296 $temp_addons = $this->get_addons();
297
298 // If error(s) occured during license key verification, display them and exit now.
299 if ( empty( $temp_addons ) ) { ?>
300
301 <h1 class="page-title"><?php echo __( 'Charitable Addons', 'charitable' ); ?></h1>
302
303 <div class="error below-h2">
304 <p>
305 <?php esc_html_e( 'There are some communication or license issues. Please make sure your server is able to communicate with wpcharitable.com. Contact Charitable Support for additional assistance.', 'charitable' ); ?>
306 </p>
307 </div>
308 <?php
309
310 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
311 error_log( 'addons_directory_content - communication issue' );
312 }
313
314 return;
315 }
316
317 $recommended_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['recommended'] ) ? $temp_addons['recommended'] : array() );
318 $unlicensed_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['unlicensed'] ) ? $temp_addons['unlicensed'] : array() );
319 $licensed_addons = array_map( array( $this, 'prepare_addon_data' ), ! empty( $temp_addons['licensed'] ) ? $temp_addons['licensed'] : array() );
320
321 $addons = array ( 'recommended' => $recommended_addons, 'licensed' => $licensed_addons, 'unlicensed' => $unlicensed_addons );
322
323 // If no Addon(s) were returned, our API call returned an error.
324 // Show an error message with a button to reload the page, which will trigger another API call.
325 if ( ! $addons ) {
326 ?>
327 <form id="charitable-addons-refresh-addons-form" method="post">
328 <p>
329 <?php esc_html_e( 'There was an issue retrieving the addons for this site. Please click on the button below the refresh the addons data.', 'charitable' ); ?>
330 </p>
331 <p>
332 <a href="<?php echo esc_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ); ?>" class="button button-primary"><?php esc_html_e( 'Refresh Addons', 'charitable' ); ?></a> <?php // @codingStandardsIgnoreLine ?>
333 </p>
334 </form>
335 <?php
336 return;
337 }
338
339 // If here, we have Addons to display, so let's output them now.
340 // Get installed plugins and upgrade URL.
341 $installed_plugins = get_plugins();
342 $upgrade_url = $this->charitable_get_upgrade_link();
343 $type = ( isset( $_GET['plan'] ) ) ? $_GET['plan'] : $this->get_current_plan_slug(); // todo: remove $_GET.
344 ?>
345
346 <h1 class="page-title"><?php echo get_admin_page_title(); ?><input type="search" placeholder="Search Addons" id="charitable-admin-addons-search"></h1>
347
348 <?php
349
350 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
351 echo '<p>Plan type is: ' . $type . '</p>';
352 error_log('addons_directory_content' );
353 error_log( print_r( $type, true ) );
354 error_log( print_r( $installed_plugins, true ) );
355 error_log( print_r( $upgrade_url, true ) );
356 }
357
358 ?>
359
360 <?php if ( ! charitable_is_pro() ) { ?>
361
362 <?php echo Charitable_Settings::get_instance()->settings_cta(); ?>
363
364 <?php } ?>
365
366 <div id="charitable-addons">
367
368 <div id="charitable-admin-addons-list">
369
370 <div class="list">
371 <?php
372 if ( isset( $addons['recommended'] ) && ! empty( $addons['recommended'] ) ) {
373 foreach ( (array) $addons['recommended'] as $i => $addon ) {
374 $this->get_addon_card( $addon, $i, true, $installed_plugins );
375 }
376 }
377 ?>
378 <?php
379 if ( isset( $addons['licensed'] ) && ! empty( $addons['licensed'] ) ) {
380 foreach ( (array) $addons['licensed'] as $i => $addon ) {
381 $this->get_addon_card( $addon, $i, true, $installed_plugins );
382 }
383 }
384 ?>
385 <?php
386 if ( isset( $addons['unlicensed'] ) && ! empty( $addons['unlicensed'] ) ) {
387 foreach ( (array) $addons['unlicensed'] as $i => $addon ) {
388 $this->get_addon_card( $addon, $i, false, $installed_plugins );
389 }
390 }
391 ?>
392 </div>
393
394 </div>
395 <?php
396
397 }
398
399 /**
400 * Retrieves addons from the stored transient or remote server.
401 *
402 * @since 1.7.0
403 *
404 * @return bool | array false | Array of licensed and unlicensed Addons.
405 */
406 public function get_addons() {
407
408 // Get license key and type.
409 $key = false; // charitable_get_license_key();
410 $type = $this->get_current_plan_slug();
411 $addons = get_transient( '_charitable_addons' ); // @codingStandardsIgnoreLine - testing.
412
413 // Get addons data from transient or perform API query if no transient.
414 if ( false === $addons ) {
415 $addons = $this->get_addons_data_from_server( $key );
416 }
417
418 // If no Addons exist, return false.
419 if ( ! $addons ) {
420 return array();
421 }
422
423 // Iterate through Addons, to build two arrays:
424 // - Addons the user is licensed to use,
425 // - Addons the user isn't licensed to use.
426 $results = array(
427 'recommended' => array(),
428 'licensed' => array(),
429 'unlicensed' => array(),
430 );
431
432 foreach ( (array) $addons as $i => $addon ) {
433
434 if ( ! isset( $addon['slug'] ) || $addon['slug'] === '' || strtolower( $addon['slug'] ) === 'auto draft' ) {
435 continue;
436 }
437
438 if ( ! is_array( $addon ) ) {
439 $addon = array();
440 }
441
442 $addon['path'] = ( '' === $addon['path'] ) ? sprintf( '%1$s/%1$s.php', $addon['slug'] ) : $addon['path'];
443
444 $type = ( isset( $_GET['license'] ) ) ? $_GET['license'] : $this->get_current_plan_slug();
445
446 if ( isset( $addon['featured'] ) && in_array( 'recommended', $addon['featured'] ) ) {
447 $results['recommended'][] = (array) $addon;
448 continue;
449 } else {
450 // Determine if the addon belongs licensed or unlicensed based on the confirmed plan.
451 if ( isset( $addon['license'] ) && in_array( $type, $addon['license'] ) ) {
452 $results['licensed'][] = (array) $addon;
453 continue;
454 } else {
455 $results['unlicensed'][] = (array) $addon;
456 continue;
457 }
458 }
459
460 }
461
462 // Return Addons, split by licensed and unlicensed.
463 return $results;
464
465 }
466
467
468 /**
469 * Stores an upgrade link used for the addons page when a person needs a pro license.
470 *
471 * @since 1.7.0.4
472 *
473 * @return string The url
474 */
475 public function charitable_get_upgrade_link() {
476 return 'https://wpcharitable.com/lite-upgrade/?discount=LITEUPGRADE&utm_source=WordPress&utm_campaign=liteplugin&utm_medium=addons&utm_content=ActiveCampaign%20Addon';
477 }
478
479 /**
480 * Retrieve the plugin basename from the plugin slug.
481 *
482 * @since 1.7.0
483 *
484 * @param string $slug The plugin slug.
485 * @return string The plugin basename if found, else the plugin slug.
486 */
487 public function get_plugin_basename_from_slug( $slug ) {
488
489 $keys = array_keys( get_plugins() );
490
491 foreach ( $keys as $key ) {
492 if ( preg_match( '|^' . $slug . '|', $key ) ) {
493 return $key;
494 }
495 }
496
497 return $slug;
498
499 }
500
501 /**
502 * Outputs the addon "box" on the addons page.
503 *
504 * @since 1.7.0
505 *
506 * @param object $addon Addon data from the API / transient call.
507 * @param int $counter Index of this Addon in the collection.
508 * @param bool $is_licensed Whether the Addon is licensed for use.
509 * @param array $installed_plugins Installed WordPress Plugins.
510 */
511 public function get_addon_card( $addon, $counter = 0, $is_licensed = false, $installed_plugins = false ) {
512
513 if ( ! isset( $addon['slug'] ) ) {
514 return;
515 }
516
517 // Setup some vars.
518 $plugin_basename = $this->get_plugin_basename_from_slug( $addon['slug'] );
519 $replacements = array ('charitable-recurring-donations' => 'charitable-recurring' );
520 if ( array_key_exists( $addon['slug'], $replacements ) ) {
521 $plugin_basename = $this->get_plugin_basename_from_slug( $replacements[ $addon['slug'] ] );
522 }
523 // $categories = implode( ',', $addon['categories'] );
524
525 if ( ! $installed_plugins ) {
526 $installed_plugins = get_plugins();
527 }
528
529 // If the Addon doesn't supply an upgrade_url key, it's because the user hasn't provided a license.
530 // get_upgrade_link() will return the Lite or Pro link as necessary for us.
531 if ( ! isset( $addon['upgrade_url'] ) ) {
532 $addon['upgrade_url'] = $this->charitable_get_upgrade_link();
533 }
534
535 // Add marketing tracking.
536 $addon['upgrade_url'] = add_query_arg(
537 array(
538 'utm_source' => 'proplugin',
539 'utm_medium' => 'addonspage',
540 'utm_campaign' => str_replace( '-', '', $addon['slug'] ) . 'addon',
541 ),
542 $addon['upgrade_url']
543 );
544
545 if ( ! isset( $installed_plugins[ $plugin_basename ] ) ) {
546 switch ( $this->get_current_plan_slug() ) {
547 case 'plus':
548 $the_plans = array( 'basic', 'plus' );
549 break;
550 case 'pro':
551 $the_plans = array( 'basic', 'plus', 'pro' );
552 break;
553 case 'agency':
554 $the_plans = array( 'basic', 'plus', 'pro', 'agency' );
555 break;
556
557 default:
558 $the_plans = array ();
559 break;
560 }
561
562 $addon['status'] = 'missing';
563 $addon['action'] = 'license';
564
565 foreach ( $addon['license'] as $license_to_check ) {
566 if ( in_array( $license_to_check, $the_plans ) ) {
567 $addon['status'] = 'missing';
568 $addon['action'] = 'install';
569 }
570 }
571
572 } else {
573 // Plugin is installed.
574 if ( is_plugin_active( $plugin_basename ) ) {
575 $addon['status'] = 'active';
576 $addon['action'] = 'deactivate';
577 } else {
578 $addon['status'] = 'installed';
579 $addon['action'] = 'activate';
580 }
581 }
582
583 $button = $this->get_addon_button_html( $addon );
584
585 $status_label = $this->get_addon_status_label( $addon['status'] );
586
587 // get the icon/graphic.
588 $icon = isset( $addon['icon'] ) ? esc_url( $addon['icon'] ) : 'placeholder';
589
590 // get the plugin description.
591 $sections = unserialize( $addon['sections'] );
592 $description = wp_strip_all_tags( ( $sections['description'] ) );
593 $is_recommended = ( isset( $addon['featured'] ) && is_array( $addon['featured'] ) && in_array( 'recommended', $addon['featured']) ) ? true : false;
594
595 // css
596 $css = array ('addon-container');
597 if ( $is_recommended ) {
598 $css[] = 'recommended';
599 }
600
601 // Output the card.
602 ?>
603
604 <div class="<?php echo implode(' ', $css ); ?>">
605 <div class="addon-item">
606 <?php if ( $is_recommended ) : ?>
607 <div class="recommended"><?php echo esc_html__( 'Recommended', 'charitable' ); ?></div>
608 <?php endif; ?>
609 <img src="<?php echo esc_url( $icon ); ?>" alt="<?php echo esc_html( $addon['name'] ); ?> Addon logo">
610 <div class="details charitable-clear">
611 <h5 class="addon-name">
612 <a href="<?php echo esc_url( $addon['upgrade_url'] ); ?>" title="Learn more" target="_blank" rel="noopener noreferrer" class="addon-link"><?php echo esc_html( $addon['name'] ); ?> Addon</a></h5>
613 <p class="addon-desc"><?php echo wpautop( $description ); ?></p>
614 </div>
615 <div class="actions charitable-clear"><?php //echo $addon['action']; ?>
616 <?php
617 if ( ! empty( $addon['status'] ) && $addon['action'] !== 'upgrade' && $addon['action'] !== 'license' && $addon['plugin_allow'] ) :
618 $action_class = 'action-button';
619 ?>
620 <div class="status">
621 <strong>
622 <?php
623 printf(
624 /* translators: %s - addon status label. */
625 esc_html__( 'Status: %s', 'charitable' ),
626 '<span class="status-label status-' . esc_attr( $addon['status'] ) . '">' . wp_kses_post( $status_label ) . '</span>'
627 );
628 ?>
629 </strong>
630 </div>
631 <?php
632 endif;
633
634 $action_class = empty( $action_class ) ? 'upgrade-button' : $action_class;
635 ?>
636 <div class="<?php echo esc_attr( $action_class ); ?>">
637 <?php echo $button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
638 </div>
639 </div>
640 </div>
641 </div>
642
643 <?php
644
645 }
646
647 /**
648 * Get addon button HTML.
649 *
650 * @since 1.6.7
651 *
652 * @param array $addon Prepared addon data.
653 *
654 * @return string
655 */
656 private function get_addon_button_html( $addon ) {
657
658 if ( $addon['action'] === 'upgrade' || $addon['action'] === 'license' || ! $addon['plugin_allow'] ) {
659
660 if ( charitable_is_pro() ) {
661
662 // go ahead and send those who have a license key added to the checkout page, but down the road we will send them to the pricing page and then customize the prices on the pricing page to reflect the upgrade amounts per plan.
663
664 $license_data_to_send = $this->get_license_key_for_upgrade();
665
666 if ( false !== $license_data_to_send && isset( $license_data_to_send['license'] ) ) {
667
668 $upid = 0; // the upgrade plan id.
669
670 if ( in_array( 'agency', $addon['license'] ) ) { // if the desired addon is in the agency category license, we need to pass along the upgrade plan id of 4
671 $upid = 4;
672 }
673
674 if ( in_array( 'pro', $addon['license'] ) ) { // if the desired addon is in the pro category license, we need to pass along the upgrade plan id of 3
675 $upid = 3;
676 }
677
678 if ( in_array( 'plus', $addon['license'] ) ) { // if the desired addon is in the plus category license, we need to pass along the upgrade plan id of 2
679 $upid = 2;
680 }
681
682 // we have a valid license that we could get an upgrade path, etc. from
683 $upgrade_url = charitable_ga_url(
684 // pid = plan_id
685 Charitable_Addons_Directory::UPDATE_URL . '/?license_upgrade=true&license_key=' . $license_data_to_send['license'] . '&upid=' . $upid . '&pid=' . $license_data_to_send['plan_id'] . '&addon=' . esc_html( $addon['name'] ),
686 urlencode( 'Plugin Addon Page' ),
687 urlencode( $addon['name'] . ' Upgrade' )
688 );
689
690 } else {
691
692 // they may be on 'pro' mode but they don't have a license we can send, so fall back to sending them to the pricing page.
693 $upgrade_url = charitable_ga_url(
694 'https://www.wpcharitable.com/pricing/',
695 urlencode( 'Plugin Addon Page' ),
696 urlencode( $addon['name'] . ' Upgrade' )
697 );
698
699 }
700
701
702 } else {
703 // they are on lite, with no license so send them to the pricing page.
704 $upgrade_url = charitable_ga_url(
705 'https://www.wpcharitable.com/pricing/',
706 urlencode( 'Plugin Addon Page' ),
707 urlencode( $addon['name'] . ' Upgrade' )
708 );
709 }
710
711 return sprintf(
712 '<a href="%1$s" target="_blank" rel="noopener noreferrer" class="charitable-btn charitable-btn-orange">%2$s</a>',
713 $upgrade_url,
714 esc_html__( 'Upgrade Now', 'charitable' )
715 );
716 }
717
718 $html = '';
719
720 // Override to account for odd setup prior to new ownership.
721 $addon['path'] = str_replace( 'charitable-recurring-donations', 'charitable-recurring', $addon['path'] );
722
723 // Apply filter, mostly for testing and troubleshooting.
724 $addon = apply_filters( 'get_addon_button_html_addon', $addon );
725
726 switch ( $addon['status'] ) {
727 case 'active':
728 $html = '<button class="status-' . esc_attr( $addon['status'] ) . '" data-plugin="' . esc_attr( $addon['path'] ) . '" data-type="addon">';
729 $html .= esc_html__( 'Deactivate', 'charitable' );
730 $html .= '</button>';
731 break;
732
733 case 'installed':
734 $html = '<button class="status-' . esc_attr( $addon['status'] ) . '" data-plugin="' . esc_attr( $addon['path'] ) . '" data-type="addon">';
735 $html .= esc_html__( 'Activate', 'charitable' );
736 $html .= '</button>';
737 break;
738
739 case 'missing':
740 if ( ! $this->can_install ) {
741 break;
742 }
743 $html = '<button class="status-' . esc_attr( $addon['status'] ) . '" data-plugin="' . esc_url( $addon['install'] ) . '" data-type="addon">';
744 $html .= esc_html__( 'Install Addon', 'charitable' );
745 $html .= '</button>';
746 break;
747 }
748
749 return $html;
750 }
751
752
753 /**
754 * Get addon status label.
755 *
756 * @since 1.6.7
757 *
758 * @param string $status Addon status.
759 *
760 * @return string
761 */
762 private function get_addon_status_label( $status ) {
763
764 $label = [
765 'active' => esc_html__( 'Active', 'charitable' ),
766 'installed' => esc_html__( 'Inactive', 'charitable' ),
767 'missing' => esc_html__( 'Not Installed', 'charitable' ),
768 ];
769
770 return isset( $label[ $status ] ) ? $label[ $status ] : '';
771 }
772
773
774 /**
775 * Prepare addon data.
776 *
777 * @since 1.6.6
778 *
779 * @param array $addon Addon data.
780 *
781 * @return array Extended addon data.
782 */
783 protected function prepare_addon_data( $addon ) {
784
785 if ( empty( $addon ) ) {
786 return [];
787 }
788
789 $addon['title'] = ! empty( $addon['title'] ) ? $addon['title'] : '';
790 $addon['slug'] = ! empty( $addon['slug'] ) ? $addon['slug'] : '';
791
792 // We need the cleared name of the addon, without the ' addon' suffix, for further use.
793 // $addon['name'] = preg_replace( '/ addon$/i', '', $addon['title'] );
794
795 /* translators: %s - addon name. */
796 $addon['modal_name'] = sprintf( esc_html__( '%s addon', 'charitable' ), $addon['name'] );
797 $addon['clear_slug'] = str_replace( 'charitable-', '', $addon['slug'] );
798 $addon['utm_content'] = ucwords( str_replace( '-', ' ', $addon['clear_slug'] ) );
799 $addon['license'] = empty( $addon['license'] ) ? [] : (array) $addon['license'];
800 $addon['license_level'] = $this->get_license_level( $addon );
801 $addon['icon'] = ! empty( $addon['icon'] ) ? $addon['icon'] : '';
802 $addon['path'] = sprintf( '%1$s/%1$s.php', $addon['slug'] );
803 $addon['video'] = ! empty( $addon['video'] ) ? $addon['video'] : '';
804 $addon['plugin_allow'] = $this->has_access( $addon );
805 $addon['status'] = 'missing';
806 $addon['action'] = 'upgrade';
807 $addon['page_url'] = empty( $addon['url'] ) ? '' : $addon['url'];
808 $addon['doc_url'] = empty( $addon['doc'] ) ? '' : $addon['doc'];
809 $addon['url'] = '';
810
811 static $nonce = '';
812 $nonce = empty( $nonce ) ? wp_create_nonce( 'charitable-admin' ) : $nonce;
813 $addon['nonce'] = $nonce;
814
815 return $addon;
816 }
817
818
819 /**
820 * Get available addon data by slug.
821 *
822 * @since 1.6.6
823 *
824 * @param string $slug Addon slug, can be both "charitable-drip" and "drip".
825 *
826 * @return array Single addon data. Empty array if addon is not found.
827 */
828 public function get_addon( $slug ) {
829
830 $slug = 'charitable-' . str_replace( 'charitable-', '', sanitize_key( $slug ) );
831
832 $addon = ! empty( $this->available_addons[ $slug ] ) ? $this->available_addons[ $slug ] : [];
833
834 // In case if addon is "not available" let's try to get and prepare addon data from all addons.
835 if ( empty( $addon ) ) {
836 $addon = ! empty( $this->addons[ $slug ] ) ? $this->prepare_addon_data( $this->addons[ $slug ] ) : [];
837 }
838
839 return $addon;
840 }
841
842 /**
843 * Get license level of the addon.
844 *
845 * @since 1.6.6
846 *
847 * @param array|string $addon Addon data array OR addon slug.
848 *
849 * @return string License level: pro | elite.
850 */
851 private function get_license_level( $addon ) {
852
853 if ( empty( $addon ) ) {
854 return '';
855 }
856
857 $addon = is_string( $addon ) ? $this->get_addon( $addon ) : $addon;
858 $addon['license'] = empty( $addon['license'] ) ? [] : (array) $addon['license'];
859
860 // TODO: convert to a class constant when we will drop PHP 5.5.
861 $levels = [ 'basic', 'plus', 'pro', 'elite', 'agency', 'ultimate' ];
862 $license = '';
863
864 foreach ( $levels as $level ) {
865 if ( in_array( $level, $addon['license'], true ) ) {
866 $license = $level;
867
868 break;
869 }
870 }
871
872 if ( empty( $license ) ) {
873 return '';
874 }
875
876 return in_array( $license, [ 'basic', 'plus', 'pro' ], true ) ? 'pro' : 'elite';
877 }
878
879 /**
880 * This attempts to send the BEST license key to Charitable whenever a suggested upgrade is possible.
881 * Note: We look for "v2" licenses in settings first, then attempt to locate a charitable basic/plus/pro/agency (new or oldschool) license.
882 * We don't return individual addons in this function because there's no good way to suggest an upgrade EDD license path.
883 *
884 * @since 1.7.0.4
885 *
886 * @return array
887 */
888 private function get_license_key_for_upgrade() {
889
890 $settings = get_option( 'charitable_settings' );
891 if ( isset( $settings['licenses']['charitable-v2']['license'] ) && ! empty( $settings['licenses']['charitable-v2']['license'] ) ) {
892
893 // attempt to grab the "v2" license
894 $license = esc_html( $settings['licenses']['charitable-v2']['license'] );
895 $plan_id = isset( $settings['licenses']['charitable-v2']['plan_id'] ) && ! empty( $settings['licenses']['charitable-v2']['plan_id'] ) ? intval( $settings['licenses']['charitable-v2']['plan_id'] ) : false;
896 return array( 'license' => $license, 'plan_id' => $plan_id );
897
898 } else if ( isset( $settings['licenses']['charitable'] ) && ! empty( $settings['licenses']['charitable'] ) ) {
899
900 $license = esc_html( $settings['licenses']['charitable'] );
901 return array( 'license' => $license, 'plan_id' => false ); // no plan id when this was added into settings.
902
903 } else if ( isset( $settings['licenses']['charitable_pro'] ) && ! empty( $settings['licenses']['charitable_pro'] ) ) {
904
905 // unlikely but technically possible.
906 $license = esc_html( $settings['licenses']['charitable_pro'] );
907 return array( 'license' => $license, 'plan_id' => false ); // no plan id when this was added into settings.
908
909 }
910
911 return false;
912
913 }
914
915 /**
916 * Determine if user's license level has access.
917 *
918 * @since 1.6.6
919 *
920 * @param array|string $addon Addon data array OR addon slug.
921 *
922 * @return bool
923 */
924 protected function has_access( $addon ) {
925
926 return true;
927
928 }
929
930
931 /**
932 * Return the latest versions of Charitable plugins.
933 *
934 * @since 1.7.0.4
935 *
936 * @return array
937 */
938 public function get_addons_data_from_server( $licenses = false ) {
939
940 $versions = false;
941
942 // if ( ! defined( 'CHARITABLE_DEBUG' ) ) {
943 $versions = get_transient( '_charitable_plugin_versions' );
944 // }
945
946 if ( false === $versions ) {
947
948 if ( false === $licenses ) {
949
950 $licenses = array();
951
952 foreach ( $this->get_licenses() as $license ) {
953 if ( isset( $license['license'] ) ) {
954 $licenses[] = $license['license'];
955 }
956 }
957
958 }
959
960 $response = wp_remote_post(
961 Charitable_Addons_Directory::UPDATE_URL . '/edd-api/versions-v3/',
962 array(
963 'sslverify' => false,
964 'timeout' => 15,
965 'body' => array(
966 'licenses' => $licenses,
967 'url' => home_url(),
968 ),
969 )
970 );
971
972 $response_body = wp_remote_retrieve_body( $response );
973 $response_code = wp_remote_retrieve_response_code( $response );
974
975 // Bail out early if there are any errors.
976 if ( (int) $response_code !== 200 || is_wp_error( $response_body ) ) {
977 return false;
978 }
979
980 $versions = json_decode( $response_body, true );
981
982 set_transient( '_charitable_plugin_versions', $versions, DAY_IN_SECONDS );
983
984 } // end if
985
986 return $versions;
987
988 }
989
990 /**
991 * Return the latest versions of Charitable plugins.
992 *
993 * @since 1.4.0
994 *
995 * @return array
996 */
997 protected function get_versions() {
998 $versions = wp_cache_get( 'plugin_versions', 'charitable' );
999
1000 if ( false === $versions ) {
1001
1002 $licenses = array();
1003
1004 foreach ( $this->get_licenses() as $license ) {
1005 if ( isset( $license['license'] ) ) {
1006 $licenses[] = $license['license'];
1007 }
1008 }
1009
1010 $response = wp_remote_post(
1011 Charitable_Addons_Directory::UPDATE_URL . '/edd-api/versions-v2/',
1012 array(
1013 'sslverify' => false,
1014 'timeout' => 15,
1015 'body' => array(
1016 'licenses' => $licenses,
1017 'url' => home_url(),
1018 ),
1019 )
1020 );
1021
1022 $versions = wp_remote_retrieve_body( $response );
1023
1024 $versions = json_decode( $versions, true );
1025
1026 wp_cache_set( 'plugin_versions', $versions, 'charitable' );
1027 } // end if
1028
1029 return $versions;
1030 }
1031
1032 /**
1033 * Return the list of licenses.
1034 *
1035 * Note: The licenses are not necessarily valid. If a user enters an invalid
1036 * license, the license will be stored but it will be flagged as invalid.
1037 *
1038 * @since 1.0.0
1039 *
1040 * @return array[]
1041 */
1042 public function get_licenses() {
1043 if ( ! isset( $this->licenses ) ) {
1044 $this->licenses = charitable_get_option( 'licenses', array() );
1045 }
1046
1047 return $this->licenses;
1048 }
1049
1050 }
1051
1052 endif;
1053