PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.8.6
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.8.6
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 1.7.0.9 1.8.0 All 210 releases
charitable / charitable.php

charitable.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.8.6, at charitable.php

1,428 lines 41.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Charitable
4 * Plugin URI: https://www.wpcharitable.com
5 * Description: The best WordPress donation plugin. Fundraising with recurring donations, and powerful features to help you raise more money online.
6 * Version: 1.8.8.6
7 * Author: Charitable Donations & Fundraising Team
8 * Author URI: https://wpcharitable.com
9 * Requires at least: 5.0
10 * Stable tag: 1.8.8.6
11 * License: GPLv2 or later
12 * License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
13 *
14 * Text Domain: charitable
15 * Domain Path: /i18n/languages/
16 *
17 * @package Charitable
18 * @author David Bisset
19 * @copyright Copyright (c) 2024, WP Charitable LLC
20 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
21 */
22
23 // Exit if accessed directly.
24 if ( ! defined( 'ABSPATH' ) ) {
25 exit;
26 }
27
28 if ( ! class_exists( 'Charitable' ) ) :
29
30 /**
31 * Main Charitable class.
32 */
33 class Charitable {
34
35 /** The product name. */
36 const NAME = 'Charitable';
37
38 /** The plugin author name. */
39 const AUTHOR = 'WP Charitable';
40
41 /* Plugin version. */
42 const VERSION = '1.8.8.6';
43
44 /* Version of database schema. */
45 const DB_VERSION = '20180522';
46
47 /* Campaign post type. */
48 const CAMPAIGN_POST_TYPE = 'campaign';
49
50 /* Donation post type. */
51 const DONATION_POST_TYPE = 'donation';
52
53 /**
54 * Single instance of this class.
55 *
56 * @since 1.0.0
57 *
58 * @var Charitable
59 */
60 private static $instance = null;
61
62 /**
63 * The absolute path to this plugin's directory.
64 *
65 * @since 1.0.0
66 *
67 * @var string
68 */
69 private $directory_path;
70
71 /**
72 * The URL of this plugin's directory.
73 *
74 * @since 1.0.0
75 *
76 * @var string
77 */
78 private $directory_url;
79
80 /**
81 * Directory path for the includes folder of the plugin.
82 *
83 * @since 1.0.0
84 *
85 * @var string
86 */
87 private $includes_path;
88
89 /**
90 * Store of registered objects.
91 *
92 * @since 1.0.0
93 * @since 1.5.0 Changed to Charitable_Registry object. Previously it was an array.
94 *
95 * @var Charitable_Registry
96 */
97 private $registry;
98
99 /**
100 * Classmap.
101 *
102 * @since 1.5.0
103 *
104 * @var array
105 */
106 private $classmap;
107
108 /**
109 * Plugin.
110 *
111 * @since 1.8.0
112 *
113 * @var array
114 */
115 private $plugin;
116
117 /**
118 * Create class instance.
119 *
120 * @since 1.0.0
121 */
122 public function __construct() {
123 $this->directory_path = plugin_dir_path( __FILE__ );
124 $this->directory_url = plugin_dir_url( __FILE__ );
125 $this->includes_path = $this->directory_path . 'includes/';
126
127 define( 'CHARITABLE_DIRECTORY_PATH', plugin_dir_path( __FILE__ ) );
128 define( 'CHARITABLE_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
129 define( 'CHARITABLE_MARKETING_URL', 'https://wpcharitable.com/' );
130
131 define( 'CHARITABLE_BUILDER_SHOW_LEGACY_EDIT_LINKS', true ); // 1.8.0 specific.
132
133 spl_autoload_register( array( $this, 'autoloader' ) );
134
135 $this->load_dependencies();
136
137 register_activation_hook( __FILE__, array( $this, 'activate' ) );
138 register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
139
140 add_filter( 'plugin_action_links_' . CHARITABLE_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ), 99, 2 );
141 add_action( 'plugins_loaded', array( $this, 'start' ), 3 );
142
143 add_action( 'plugins_loaded', array( $this, 'check_for_version_conflicts' ), 2 );
144
145 // Initialize error handler early.
146 add_action(
147 'plugins_loaded',
148 function () {
149 require_once $this->get_path( 'includes' ) . 'class-charitable-error-handler.php';
150 $error_handler = new Charitable_Error_Handler();
151 $error_handler->init();
152 },
153 1
154 );
155 }
156
157 /**
158 * Returns the original instance of this class.
159 *
160 * @since 1.0.0
161 *
162 * @return Charitable
163 */
164 public static function get_instance() {
165 return self::$instance;
166 }
167
168 /**
169 * Run the startup sequence.
170 *
171 * This is only ever executed once.
172 *
173 * @since 1.0.0
174 *
175 * @return void
176 */
177 public function start() {
178 /* If we've already started (i.e. run this function once before), do not pass go. */
179 if ( $this->started() ) {
180 return;
181 }
182
183 /* Set static instance. */
184 self::$instance = $this;
185
186 /* Set up the registry and instantiate objects we need straight away. */
187 $this->registry();
188
189 $this->setup_licensing();
190
191 $this->maybe_start_ajax();
192
193 $this->attach_hooks_and_filters();
194
195 $this->maybe_start_admin();
196
197 $this->maybe_start_public();
198
199 Charitable_Addons::load( $this );
200 }
201
202 /**
203 * Include necessary files.
204 *
205 * @since 1.0.0
206 *
207 * @return void
208 */
209 private function load_dependencies() {
210 $includes_path = $this->get_path( 'includes' );
211
212 /* Load files with hooks & functions. Classes are autoloaded. */
213 require_once $includes_path . 'charitable-core-functions.php';
214 require_once $includes_path . 'api/charitable-api-functions.php';
215 require_once $includes_path . 'campaigns/charitable-campaign-functions.php';
216 require_once $includes_path . 'campaigns/charitable-campaign-hooks.php';
217 require_once $includes_path . 'compat/charitable-compat-functions.php';
218 require_once $includes_path . 'currency/charitable-currency-functions.php';
219 require_once $includes_path . 'deprecated/charitable-deprecated-functions.php';
220 require_once $includes_path . 'donations/charitable-donation-hooks.php';
221 require_once $includes_path . 'donations/charitable-donation-functions.php';
222 require_once $includes_path . 'emails/charitable-email-hooks.php';
223 require_once $includes_path . 'endpoints/charitable-endpoints-functions.php';
224 require_once $includes_path . 'privacy/charitable-privacy-functions.php';
225 require_once $includes_path . 'public/charitable-template-helpers.php';
226 require_once $includes_path . 'shortcodes/charitable-shortcodes-hooks.php';
227 require_once $includes_path . 'upgrades/charitable-upgrade-hooks.php';
228 require_once $includes_path . 'users/charitable-user-functions.php';
229 require_once $includes_path . 'user-management/charitable-user-management-hooks.php';
230 require_once $includes_path . 'utilities/charitable-utility-functions.php';
231 require_once $includes_path . 'elementor/charitable-elementor-hooks.php';
232
233 /* Load vendor/autoload.php - our modified platform_check.php and autoload_real.php will handle conditional loading */
234 require_once $this->get_path( 'directory' ) . 'vendor/autoload.php';
235
236 /* Square Compatibility Wrapper - always load this regardless of PHP version */
237 require_once $includes_path . 'square/class-charitable-square-compatibility.php';
238
239 /* Initialize Square conditionally - will check for compatibility internally */
240 Charitable_Square_Compatibility::init();
241
242 if ( $this->load_core_stripe() ) :
243
244 /* Interfaces */
245 require_once $includes_path . 'stripe/interfaces/interface-charitable-stripe-gateway-processor.php';
246
247 /* Abstract classes */
248 require_once $includes_path . 'stripe/abstracts/class-charitable-stripe-gateway-processor.php';
249
250 /* Classes */
251 require_once $includes_path . 'stripe/donations/class-charitable-stripe-donation-log.php';
252 require_once $includes_path . 'stripe/donations/class-charitable-stripe-recurring-donation-log.php';
253 require_once $includes_path . 'stripe/i18n/class-charitable-stripe-i18n.php';
254 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-connected-customer.php';
255 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-customer.php';
256 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-payment-intents.php';
257 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-payment-intent.php';
258 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-plan.php';
259 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-product.php';
260
261 require_once $includes_path . 'stripe/connect/charitable-stripe-connect-functions.php';
262 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-checkout.php';
263
264 /* Functions & Hooks */
265 require_once $includes_path . 'stripe/charitable-stripe-core-functions.php';
266 require_once $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php';
267 require_once $includes_path . 'stripe/gateway/charitable-stripe-gateway-hooks.php';
268
269 /* webhooks */
270 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php';
271 require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-processor.php';
272
273 endif;
274 }
275
276 /**
277 * Load the template functions after theme is loaded.
278 *
279 * This gives themes time to override the functions.
280 *
281 * @since 1.6.10
282 *
283 * @return void
284 */
285 public function load_template_files() {
286 $includes_path = $this->get_path( 'includes' );
287
288 require_once $includes_path . 'public/charitable-template-functions.php';
289 require_once $includes_path . 'public/charitable-template-hooks.php';
290 }
291
292 /**
293 * Dynamically loads the class attempting to be instantiated elsewhere in the
294 * plugin by looking at the $class_name parameter being passed as an argument.
295 *
296 * @since 1.5.0
297 *
298 * @param string $class_name The fully-qualified name of the class to load.
299 * @return boolean
300 */
301 public function autoloader( $class_name ) {
302 /* If the specified $class_name already exists, bail. */
303 if ( class_exists( $class_name ) ) {
304 return false;
305 }
306
307 /* If the specified $class_name does not include our namespace, duck out. */
308 if ( false === strpos( $class_name, 'Charitable_' ) ) {
309 return false;
310 }
311
312 /* Autogenerated class map. */
313 if ( ! isset( $this->classmap ) ) {
314 $this->classmap = include 'includes/autoloader/charitable-class-map.php';
315
316 $this->classmap['Charitable_Gateway_Stripe_AM'] = 'gateways/class-charitable-gateway-stripe-am.php';
317 $this->classmap['Charitable_Gateway_Stripe'] = 'gateways/class-charitable-gateway-stripe-am.php';
318 }
319
320 $file_path = isset( $this->classmap[ $class_name ] ) ? $this->get_path( 'includes' ) . $this->classmap[ $class_name ] : false;
321
322 if ( false !== $file_path && file_exists( $file_path ) && is_file( $file_path ) ) {
323 require_once $file_path;
324 return true;
325 }
326 return false;
327 }
328
329 /**
330 * Returns a registered class object.
331 *
332 * @since 1.5.0
333 *
334 * @return Charitable_Registry
335 */
336 public function registry() {
337 if ( ! isset( $this->registry ) ) {
338 $this->registry = new Charitable_Registry();
339 $this->registry->register_object( Charitable_Emails::get_instance() );
340 $this->registry->register_object( Charitable_Request::get_instance() );
341 $this->registry->register_object( Charitable_Gateways::get_instance() );
342 $this->registry->register_object( Charitable_i18n::get_instance() );
343 $this->registry->register_object( Charitable_Post_Types::get_instance() );
344 $this->registry->register_object( Charitable_Cron::get_instance() );
345 $this->registry->register_object( Charitable_Widgets::get_instance() );
346 $this->registry->register_object( Charitable_Licenses::get_instance() );
347 $this->registry->register_object( Charitable_User_Dashboard::get_instance() );
348 $this->registry->register_object( Charitable_Locations::get_instance() );
349 $this->registry->register_object( Charitable_Currency::get_instance() );
350 $this->registry->register_object( Charitable_Notifications::get_instance() );
351
352 $this->registry->register_object( new Charitable_Privacy() );
353 $this->registry->register_object( new Charitable_Debugging() );
354 $this->registry->register_object( new Charitable_Locale() );
355 }
356
357 return $this->registry;
358 }
359
360 /**
361 * Set up hook and filter callback functions.
362 *
363 * @since 1.0.0
364 *
365 * @return void
366 */
367 private function attach_hooks_and_filters() {
368 add_action( 'wpmu_new_blog', array( $this, 'maybe_activate_charitable_on_new_site' ) );
369 add_action( 'plugins_loaded', array( $this, 'charitable_install' ), 100 );
370 add_action( 'plugins_loaded', array( $this, 'charitable_start' ), 100 );
371 add_action( 'plugins_loaded', array( $this, 'endpoints' ), 100 );
372 add_action( 'init', array( $this, 'donation_fields' ), 100 );
373 add_action( 'init', array( $this, 'campaign_fields' ), 100 );
374 add_action( 'plugins_loaded', array( $this, 'register_donormeta_table' ) );
375 add_action( 'plugins_loaded', 'charitable_load_compat_functions' );
376 add_action( 'setup_theme', array( 'Charitable_Customizer', 'start' ) );
377 add_action( 'after_setup_theme', array( $this, 'load_template_files' ) );
378 add_action( 'wp_enqueue_scripts', array( $this, 'maybe_start_qunit' ), 100 );
379 add_action( 'rest_api_init', 'charitable_register_api_routes' );
380
381 /**
382 * We do this on priority 20 so that any functionality that is loaded on init (such
383 * as addons) has a chance to run before the event.
384 */
385 add_action( 'init', array( $this, 'do_charitable_actions' ), 20 );
386
387 if ( $this->load_core_stripe() ) :
388
389 /**
390 * Set up scripts & stylesheets & enqueue them when appropriate.
391 */
392 add_action( 'charitable_form_after_fields', array( $this, 'maybe_setup_stripe_scripts_in_donation_form' ) );
393 add_action( 'charitable_campaign_loop_after', array( $this, 'maybe_setup_stripe_scripts_in_campaign_loop' ) );
394
395 endif;
396 }
397
398
399 /**
400 * Load Stripe JS, as well as our handling scripts.
401 *
402 * @since 1.4.0
403 *
404 * @return boolean
405 */
406 public function enqueue_stripe_scripts() {
407 if ( ! Charitable_Gateways::get_instance()->is_active_gateway( Charitable_Gateway_Stripe_AM::get_gateway_id() ) ) {
408 return false;
409 }
410
411 wp_enqueue_script( 'charitable-stripe' );
412
413 return true;
414 }
415
416 /**
417 * Load Stripe JS or Stripe Checkout, as well as our handling script.
418 *
419 * @uses Charitable_Stripe::enqueue_scripts()
420 *
421 * @since 1.4.0
422 *
423 * @param Charitable_Donation_Form $form The current form object.
424 * @return boolean
425 */
426 public function maybe_setup_stripe_scripts_in_donation_form( $form ) {
427 if ( ! is_a( $form, 'Charitable_Donation_Form' ) ) {
428 return false;
429 }
430
431 if ( 'make_donation' !== $form->get_form_action() ) {
432 return false;
433 }
434
435 return $this->enqueue_stripe_scripts();
436 }
437
438 /**
439 * Enqueue the Stripe JS/Checkout scripts after a campaign loop if modal donations are in use.
440 *
441 * @uses Charitable_Stripe::enqueue_scripts()
442 *
443 * @since 1.4.0
444 *
445 * @return boolean
446 */
447 public function maybe_setup_stripe_scripts_in_campaign_loop() {
448 if ( 'modal' !== charitable_get_option( 'donation_form_display', 'separate_page' ) ) {
449 return false;
450 }
451
452 return $this->enqueue_stripe_scripts();
453 }
454
455 /**
456 * Checks whether we're in the admin area and if so, loads the admin-only functionality.
457 *
458 * @since 1.0.0
459 *
460 * @return Charitable_Admin|false
461 */
462 private function maybe_start_admin() {
463 if ( ! is_admin() ) {
464 return false;
465 }
466
467 // load blocks/Gutenberg related.
468 require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
469
470 require_once $this->get_path( 'admin' ) . 'class-charitable-admin.php';
471 require_once $this->get_path( 'admin' ) . 'charitable-admin-hooks.php';
472
473 // load campaign builder.
474 require_once $this->get_path( 'admin' ) . 'campaign-builder/util.php';
475 require_once $this->get_path( 'admin' ) . 'campaign-builder/functions.php';
476 require_once $this->get_path( 'admin' ) . 'campaign-builder/ajax-actions.php';
477 require_once $this->get_path( 'admin' ) . 'campaign-builder/functions-legacy.php';
478 require_once $this->get_path( 'admin' ) . 'campaign-builder/debug.php';
479 require_once $this->get_path( 'admin' ) . 'campaign-builder/access.php';
480 require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
481 require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
482
483 // connect for upgrade.wpcharitable.com.
484 require_once $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
485
486 // misc.
487 require_once $this->get_path( 'admin' ) . 'class-charitable-admin-pointers.php';
488 require_once $this->get_path( 'admin' ) . 'plugins/charitable-admin-plugin-hooks.php';
489 require_once $this->get_path( 'admin' ) . 'class-charitable-admin-getting-started.php';
490
491 $admin = Charitable_Admin::get_instance();
492 $this->registry->register_object( $admin );
493
494 // Stripe.
495 require_once $this->get_path( 'includes' ) . 'stripe/admin/class-charitable-stripe-admin.php';
496 new Charitable_Stripe_Admin();
497
498 return $admin;
499 }
500
501 /**
502 * Checks whether we're on the public-facing side and if so, loads the public-facing functionality.
503 *
504 * @since 1.0.0
505 *
506 * @return Charitable_Public|false
507 */
508 private function maybe_start_public() {
509 if ( is_admin() && ! $this->is_ajax() ) {
510 return false;
511 }
512
513 require_once $this->get_path( 'public' ) . 'class-charitable-public.php';
514
515 // Campaign Builder related (v1.8.0).
516 require_once $this->get_path( 'admin' ) . 'campaign-builder/class-campaign-builder-preview.php';
517 require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
518 require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
519
520 // load blocks/Gutenberg related.
521 require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
522
523 $public = Charitable_Public::get_instance();
524
525 $this->registry->register_object( $public );
526
527 return $public;
528 }
529
530 /**
531 * Load the QUnit tests if ?qunit is appended to the request.
532 *
533 * @since 1.4.17
534 *
535 * @return boolean
536 */
537 public function maybe_start_qunit() {
538 /* Skip out early if ?qunit isn't included in the request. */
539 if ( ! array_key_exists( 'qunit', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
540 return false;
541 }
542
543 /* The unit tests have to exist. */
544 if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
545 return false;
546 }
547
548 /**
549 * Register QUnit testing library.
550 *
551 * Note: These files must be bundled locally for WordPress.org compliance.
552 * Download from:
553 * - JS: https://code.jquery.com/qunit/qunit-2.3.3.js
554 * - CSS: https://code.jquery.com/qunit/qunit-2.3.3.css
555 * Save to:
556 * - assets/js/libraries/qunit-2.3.3.js
557 * - assets/css/libraries/qunit-2.3.3.css
558 *
559 * @since 1.0.0
560 * @version 1.8.8.6
561 */
562 wp_register_script(
563 'qunit',
564 $this->get_path( 'directory', false ) . 'assets/js/libraries/qunit-2.3.3.js',
565 array(),
566 '2.3.3',
567 true
568 );
569
570 /* Version: '20170615-15:44' */
571 wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
572 wp_enqueue_script( 'qunit-tests' );
573
574 wp_register_style(
575 'qunit',
576 $this->get_path( 'directory', false ) . 'assets/css/libraries/qunit-2.3.3.css',
577 array(),
578 '2.3.3',
579 'all'
580 );
581 wp_enqueue_style( 'qunit' );
582 }
583
584 /**
585 * Checks whether the current request is an AJAX request.
586 *
587 * @since 1.5.0
588 *
589 * @return boolean
590 */
591 private function is_ajax() {
592 return false !== ( defined( 'DOING_AJAX' ) && DOING_AJAX );
593 }
594
595 /**
596 * Checks whether we're executing an AJAX hook and if so, loads some AJAX functionality.
597 *
598 * @since 1.0.0
599 *
600 * @return void
601 */
602 private function maybe_start_ajax() {
603 if ( ! $this->is_ajax() ) {
604 return;
605 }
606
607 require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php';
608 require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-hooks.php';
609 }
610
611 /**
612 * This method is fired after all plugins are loaded and simply fires the charitable_start hook.
613 *
614 * Extensions can use the charitable_start event to load their own functionality.
615 *
616 * @since 1.0.0
617 *
618 * @return void
619 */
620 public function charitable_start() {
621 do_action( 'charitable_start', $this );
622 }
623
624 /**
625 * Fires off an action right after Charitable is installed, allowing other
626 * plugins/themes to do something at this point.
627 *
628 * @since 1.0.1
629 * @version 1.8.1.12 - Added transient for activation redirect.
630 * @version 1.8.3 - Added call to add version and delete notifications.
631 *
632 * @return void
633 */
634 public function charitable_install() {
635 $install = get_transient( 'charitable_install' );
636
637 if ( ! $install ) {
638 return;
639 }
640
641 require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
642
643 Charitable_Install::finish_installing();
644
645 do_action( 'charitable_install' );
646
647 delete_transient( 'charitable_install' );
648
649 // Store the date when the initial activation was performed.
650 $type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'pro' : 'lite';
651 $activated = (array) get_option( 'charitable_activated', array() );
652
653 // If no activation date is set, assume it's a new install and set the activation redirect transient.
654 if ( false === $activated || empty( $activated ) ) {
655 // Add transient to trigger redirect to the Welcome / Getting Started screen.
656 set_transient( 'charitable_activation_redirect', true, 30 );
657 }
658
659 $this->add_version_to_upgraded_from();
660
661 if ( empty( $activated[ $type ] ) ) {
662 $activated[ $type ] = time();
663 update_option( 'charitable_activated', $activated );
664 }
665
666 // Clear notifications.
667 delete_option( 'charitable_notifications' );
668 }
669
670 /**
671 * Returns whether we are currently in the start phase of the plugin.
672 *
673 * @since 1.0.0
674 *
675 * @return boolean
676 */
677 public function is_start() {
678 return current_filter() == 'charitable_start'; // phpcs:ignore
679 }
680
681 /**
682 * Returns whether the plugin has already started.
683 *
684 * @since 1.0.0
685 *
686 * @return boolean
687 */
688 public function started() {
689 return did_action( 'charitable_start' ) || current_filter() == 'charitable_start'; // phpcs:ignore
690 }
691
692 /**
693 * Returns whether the plugin is being activated.
694 *
695 * @since 1.0.0
696 *
697 * @return boolean
698 */
699 public function is_activation() {
700 return current_filter() == 'activate_charitable/charitable.php'; // phpcs:ignore
701 }
702
703 /**
704 * Returns whether the plugin is being deactivated.
705 *
706 * @since 1.0.0
707 *
708 * @return boolean
709 */
710 public function is_deactivation() {
711 return current_filter() == 'deactivate_charitable/charitable.php'; // phpcs:ignore
712 }
713
714 /**
715 * Returns plugin paths.
716 *
717 * @since 1.0.0
718 *
719 * @param string $type If empty, returns the path to the plugin.
720 * @param boolean $absolute_path If true, returns the file system path. If false, returns it as a URL.
721 * @return string
722 */
723 public function get_path( $type = '', $absolute_path = true ) {
724 $base = $absolute_path ? $this->directory_path : $this->directory_url;
725
726 switch ( $type ) {
727 case 'includes':
728 $path = $base . 'includes/';
729 break;
730
731 case 'admin':
732 $path = $base . 'includes/admin/';
733 break;
734
735 case 'public':
736 $path = $base . 'includes/public/';
737 break;
738
739 case 'assets':
740 $path = $base . 'assets/';
741 break;
742
743 case 'templates':
744 $path = $base . 'templates/';
745 break;
746
747 case 'plugin-directory':
748 $path = WP_PLUGIN_DIR;
749 break;
750
751 case 'directory':
752 $path = $base;
753 break;
754
755 default:
756 $path = __FILE__;
757
758 }//end switch
759
760 return $path;
761 }
762
763 /**
764 * Returns the plugin's version number.
765 *
766 * @since 1.0.0
767 *
768 * @return string
769 */
770 public function get_version() {
771 $version = self::VERSION;
772
773 if ( false !== strpos( $version, '-' ) ) {
774 $parts = explode( '-', $version );
775 $version = $parts[0];
776 }
777
778 return $version;
779 }
780
781 /**
782 * Adds previous versions to the upgraded_from option.
783 *
784 * @since 1.8.3
785 */
786 public function add_version_to_upgraded_from() {
787
788 $upgraded_from = get_option( 'charitable_version_upgraded_from', array() );
789
790 if ( ! is_array( $upgraded_from ) ) {
791 $upgraded_from = array();
792 }
793
794 $upgraded_from[] = $this->get_version();
795
796 update_option( 'charitable_version_upgraded_from', $upgraded_from, false );
797 }
798
799 /**
800 * Get the campaign fields registry
801 *
802 * If the registry has not been set up yet, this will also
803 * perform the task of setting it up initially.
804 *
805 * This method is called on the plugins_loaded hook.
806 *
807 * @since 1.6.0
808 *
809 * @return Charitable_Campaign_Field_Registry
810 */
811 public function campaign_fields() {
812 if ( ! $this->registry->has( 'campaign_field_registry' ) ) {
813 $campaign_fields = new Charitable_Campaign_Field_Registry();
814
815 /* Register it immediately to avoid endless recursion. */
816 $this->registry->register_object( $campaign_fields );
817
818 $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/campaign-fields.php';
819
820 foreach ( $fields as $key => $args ) {
821 $campaign_fields->register_field( new Charitable_Campaign_Field( $key, $args ) );
822 }
823 }
824
825 return $this->registry->get( 'campaign_field_registry' );
826 }
827
828 /**
829 * Get the donation fields registry
830 *
831 * If the registry has not been set up yet, this will also
832 * perform the task of setting it up initially.
833 *
834 * This method is called on the plugins_loaded hook.
835 *
836 * @since 1.5.0
837 *
838 * @return Charitable_Donation_Field_Registry
839 */
840 public function donation_fields() {
841 if ( ! $this->registry->has( 'donation_field_registry' ) ) {
842 $donation_fields = new Charitable_Donation_Field_Registry();
843
844 /* Register it immediately to avoid endless recursion. */
845 $this->registry->register_object( $donation_fields );
846
847 $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/donation-fields.php';
848
849 foreach ( $fields as $key => $args ) {
850 $donation_fields->register_field( new Charitable_Donation_Field( $key, $args ) );
851 }
852 }
853
854 return $this->registry->get( 'donation_field_registry' );
855 }
856
857 /**
858 * Return the Endpoints API object.
859 *
860 * If the Endpoints API has not been set up yet, this will also
861 * perform the task of setting it up initially.
862 *
863 * This method is called on the plugins_loaded hook.
864 *
865 * @since 1.5.0
866 *
867 * @return Charitable_Endpoints
868 */
869 public function endpoints() {
870 if ( ! $this->registry->has( 'endpoints' ) ) {
871 /**
872 * The order in which we register endpoints is important, because
873 * it determines the order in which the endpoints are checked to
874 * find whether they are the current page.
875 *
876 * Any endpoint that builds on another endpoint should be registered
877 * BEFORE the endpoint it builds on. In other words, move from
878 * most specific to least specific.
879 */
880 $endpoints = new Charitable_Endpoints();
881 $endpoints->register( new Charitable_Campaign_Donation_Endpoint() );
882 $endpoints->register( new Charitable_Campaign_Widget_Endpoint() );
883 $endpoints->register( new Charitable_Campaign_Endpoint() );
884 $endpoints->register( new Charitable_Donation_Cancellation_Endpoint() );
885 $endpoints->register( new Charitable_Donation_Processing_Endpoint() );
886 $endpoints->register( new Charitable_Donation_Receipt_Endpoint() );
887 $endpoints->register( new Charitable_Email_Preview_Endpoint() );
888 $endpoints->register( new Charitable_Email_Verification_Endpoint() );
889 $endpoints->register( new Charitable_Forgot_Password_Endpoint() );
890 $endpoints->register( new Charitable_Reset_Password_Endpoint() );
891 $endpoints->register( new Charitable_Registration_Endpoint() );
892 $endpoints->register( new Charitable_Login_Endpoint() );
893 $endpoints->register( new Charitable_Profile_Endpoint() );
894 $endpoints->register( new Charitable_Webhook_Listener_Endpoint() );
895
896 $this->registry->register_object( $endpoints );
897 }
898
899 return $this->registry->get( 'endpoints' );
900 }
901
902 /**
903 * Returns a registered object.
904 *
905 * @since 1.0.0
906 *
907 * @param string $class The type of class to be retrieved.
908 * @return object
909 */
910 public function get_registered_object( $class ) { // phpcs:ignore
911 return $this->registry->get( $class );
912 }
913
914 /**
915 * Registers our donormeta table as a meta data table.
916 *
917 * @since 1.6.0
918 *
919 * @global WPDB $wpdb
920 * @return void
921 */
922 public function register_donormeta_table() {
923 global $wpdb;
924
925 $wpdb->donormeta = $wpdb->prefix . 'charitable_donormeta';
926 }
927
928 /**
929 * Returns the model for one of Charitable's database tables.
930 *
931 * @since 1.0.0
932 *
933 * @param string $table The database table to retrieve.
934 * @return Charitable_DB
935 */
936 public function get_db_table( $table ) {
937 $tables = $this->get_tables();
938
939 if ( ! isset( $tables[ $table ] ) ) {
940 charitable_get_deprecated()->doing_it_wrong(
941 __METHOD__,
942 sprintf( 'Invalid table %s passed', $table ),
943 '1.0.0'
944 );
945 return null;
946 }
947
948 return $this->registry->get( $tables[ $table ] );
949 }
950
951 /**
952 * Return the filtered list of registered tables.
953 *
954 * @since 1.0.0
955 *
956 * @return string[]
957 */
958 private function get_tables() {
959 /**
960 * Filter the array of available Charitable table classes.
961 *
962 * @since 1.0.0
963 * @version 1.8.1
964 *
965 * @param array $tables List of tables as a key=>value array.
966 */
967 return apply_filters(
968 'charitable_db_tables',
969 array(
970 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
971 'donors' => 'Charitable_Donors_DB',
972 'donormeta' => 'Charitable_Donormeta_DB',
973 'donation_activities' => 'Charitable_Donation_Activities_DB',
974 'campaign_activities' => 'Charitable_Campaign_Activities_DB',
975 )
976 );
977 }
978
979 /**
980 * Maybe activate Charitable when a new site is added in a multisite network.
981 *
982 * @since 1.4.6
983 *
984 * @param int $blog_id The blog to activate Charitable on.
985 * @return void
986 */
987 public function maybe_activate_charitable_on_new_site( $blog_id ) {
988 if ( is_plugin_active_for_network( basename( $this->directory_path ) . '/charitable.php' ) ) {
989 switch_to_blog( $blog_id );
990 $this->activate( false );
991 restore_current_blog();
992 }
993 }
994
995 /**
996 * Runs on plugin activation.
997 *
998 * @see register_activation_hook
999 *
1000 * @since 1.0.0
1001 *
1002 * @param boolean $network_wide Whether to enable the plugin for all sites in the network
1003 * or just the current site. Multisite only. Default is false.
1004 * @return void
1005 */
1006 public function activate( $network_wide = false ) {
1007 require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
1008
1009 if ( is_multisite() && $network_wide ) {
1010 global $wpdb;
1011
1012 foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) { // phpcs:ignore
1013 switch_to_blog( $blog_id );
1014 new Charitable_Install( $this->includes_path );
1015 restore_current_blog();
1016 }
1017 } else {
1018 new Charitable_Install( $this->includes_path );
1019 }
1020
1021 // create or update the install date for future reference.
1022 update_option( 'wpcharitable_activated_datetime', current_time( 'timestamp' ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1023
1024 // Set initial timestamps for rotating menu items if they're already installed
1025 $this->set_initial_rotating_menu_timestamps();
1026 }
1027
1028 /**
1029 * Runs on plugin deactivation.
1030 *
1031 * @see register_deactivation_hook
1032 *
1033 * @since 1.0.0
1034 *
1035 * @return void
1036 */
1037 public function deactivate() {
1038 require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php';
1039 new Charitable_Uninstall();
1040 }
1041
1042 /**
1043 * If a charitable_action event is triggered, delegate the event using do_action.
1044 *
1045 * @since 1.0.0
1046 *
1047 * @return void
1048 */
1049 public function do_charitable_actions() {
1050 if ( isset( $_REQUEST['charitable_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1051
1052 $action = esc_attr( $_REQUEST['charitable_action'] ); // phpcs:ignore
1053
1054 /**
1055 * Handle Charitable action.
1056 *
1057 * @since 1.0.0
1058 */
1059 do_action( 'charitable_' . $action );
1060 }
1061 }
1062
1063 /**
1064 * Throw error on object clone.
1065 *
1066 * This class is specifically designed to be instantiated once. You can retrieve the instance using charitable()
1067 *
1068 * @since 1.0.0
1069 *
1070 * @return void
1071 */
1072 public function __clone() {
1073 charitable_get_deprecated()->doing_it_wrong(
1074 __FUNCTION__,
1075 __( 'Cloning this object is forbidden.', 'charitable' ),
1076 '1.0.0'
1077 );
1078 }
1079
1080 /**
1081 * Disable unserializing of the class.
1082 *
1083 * @since 1.0.0
1084 *
1085 * @return void
1086 */
1087 public function __wakeup() {
1088 charitable_get_deprecated()->doing_it_wrong(
1089 __FUNCTION__,
1090 __( 'Unserializing instances of this class is forbidden.', 'charitable' ),
1091 '1.0.0'
1092 );
1093 }
1094
1095 /**
1096 * DEPRECATED METHODS
1097 */
1098
1099 /**
1100 * Load plugin compatibility files on plugins_loaded hook.
1101 *
1102 * @deprecated 1.8.0
1103 *
1104 * @since 1.4.18
1105 * @since 1.5.0 Deprecated.
1106 *
1107 * @return void
1108 */
1109 public function load_plugin_compat_files() {
1110 charitable_get_deprecated()->deprecated_function(
1111 __METHOD__,
1112 '1.5.0.',
1113 'charitable_load_compat_functions'
1114 );
1115
1116 charitable_load_compat_functions();
1117 }
1118
1119 /**
1120 * Stores an object in the plugin's registry.
1121 *
1122 * @deprecated 1.8.0
1123 *
1124 * @since 1.0.0
1125 * @since 1.5.0 Deprecated.
1126 *
1127 * @param mixed $object Object to be registered.
1128 * @return void
1129 */
1130 public function register_object( $object ) { // phpcs:ignore
1131 charitable_get_deprecated()->deprecated_function(
1132 __METHOD__,
1133 '1.5.0',
1134 'charitable()->registry()->register_object()'
1135 );
1136
1137 $this->registry->register_object( $object );
1138 }
1139
1140 /**
1141 * Returns the public class.
1142 *
1143 * @deprecated 1.8.0
1144 *
1145 * @since 1.0.0
1146 * @since 1.5.0 Deprecated.
1147 *
1148 * @return Charitable_Public
1149 */
1150 public function get_public() {
1151 charitable_get_deprecated()->deprecated_function(
1152 __METHOD__,
1153 '1.5.0',
1154 'charitable_get_helper'
1155 );
1156
1157 return $this->registry->get( 'Charitable_Public' );
1158 }
1159
1160 /**
1161 * Returns the admin class.
1162 *
1163 * @deprecated 1.8.0
1164 *
1165 * @since 1.0.0
1166 * @since 1.5.0 Deprecated.
1167 *
1168 * @return Charitable_Admin
1169 */
1170 public function get_admin() {
1171 charitable_get_deprecated()->deprecated_function(
1172 __METHOD__,
1173 '1.5.0',
1174 'charitable_get_helper'
1175 );
1176
1177 return $this->registry->get( 'Charitable_Admin' );
1178 }
1179
1180 /**
1181 * Return the current request object.
1182 *
1183 * @deprecated 1.8.0
1184 *
1185 * @since 1.0.0
1186 * @since 1.5.0 Deprecated.
1187 *
1188 * @return Charitable_Request
1189 */
1190 public function get_request() {
1191 charitable_get_deprecated()->deprecated_function(
1192 __METHOD__,
1193 '1.5.0',
1194 'charitable_get_helper'
1195 );
1196
1197 return $this->registry->get( 'Charitable_Request' );
1198 }
1199
1200 /**
1201 * Returns the instance of Charitable_Currency.
1202 *
1203 * @deprecated 1.7.0
1204 *
1205 * @since 1.4.0 Deprecated.
1206 */
1207 public function get_currency_helper() {
1208 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.4.0', 'charitable_get_currency_helper' );
1209 return charitable_get_currency_helper();
1210 }
1211
1212 /**
1213 * Returns the instance of Charitable_Locations.
1214 *
1215 * @deprecated 1.6.0
1216 *
1217 * @since 1.2.0 Deprecated.
1218 */
1219 public function get_location_helper() {
1220 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.2.0', 'charitable_get_location_helper' );
1221 return charitable_get_location_helper();
1222 }
1223
1224 /**
1225 * Determine if we use Stripe core files or use an addon if that's installed and active. Checks for USE_NEW_STRIPE first.
1226 *
1227 * @since 1.7.0
1228 *
1229 * @return boolean
1230 */
1231 public function load_core_stripe() {
1232
1233 if ( false !== ( defined( 'USE_NEW_STRIPE' ) && USE_NEW_STRIPE ) ) {
1234 return true;
1235 }
1236
1237 // check and see if the core Stripe AM gateway is an active gateway, and if it is not, don't load the core stripe JS, etc.
1238 if ( class_exists( 'Charitable_Gateways' ) && class_exists( 'Charitable_Gateway_Stripe_AM' ) && ! Charitable_Gateways::get_instance()->is_active_gateway( Charitable_Gateway_Stripe_AM::get_gateway_id() ) ) {
1239 return false;
1240 }
1241
1242 return true;
1243 }
1244
1245 /**
1246 * Determine Stripe Connect is active.
1247 *
1248 * @since 1.7.0
1249 *
1250 * @return boolean
1251 */
1252 public function is_stripe_connect_addon() {
1253
1254 if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1255 return true;
1256 }
1257
1258 return false;
1259 }
1260
1261 /**
1262 * Action links for the plugins page.
1263 *
1264 * @since 1.7.0
1265 *
1266 * @param array $actions An array of existing actions.
1267 * @param string $plugin_file The plugin file we are modifying.
1268 * @param boolean $action_links An array of action links to add.
1269 * @return array An array of action links.
1270 */
1271 public function plugin_action_links( $actions, $plugin_file, $action_links = false ) {
1272
1273 if ( ! charitable_is_pro() ) :
1274
1275 // if this isn't pro, add the upgrade link which is visible on the plugins page until the plugin name.
1276
1277 $action_links = array(
1278 'proupgrade' => array(
1279 // Translators: This is an action link users can click to purchase a license for All in One SEO Pro.
1280 'label' => __( 'Upgrade to Pro', 'charitable' ),
1281 'url' => 'https://wpcharitable.com/lite-vs-pro/?utm_source=WordPress&utm_campaign=WP+Charitable&utm_medium=Action+Link+Plugins+Page&utm_content=Upgrade+to+Pro',
1282 ),
1283 );
1284
1285 endif;
1286
1287 if ( isset( $actions['edit'] ) ) {
1288 unset( $actions['edit'] );
1289 }
1290
1291 return $this->parse_action_links( $actions, $plugin_file, $action_links, 'before' );
1292 }
1293
1294 /**
1295 * Parse the action links.
1296 *
1297 * @since 1.7.0
1298 *
1299 * @param array $actions An array of existing actions.
1300 * @param string $plugin_file The plugin file we are modifying.
1301 * @param array $action_links An array of action links to add.
1302 * @param string $position The position to add the action links.
1303 * @return array
1304 */
1305 protected function parse_action_links( $actions, $plugin_file, $action_links = array(), $position = 'after' ) {
1306 if ( empty( $this->plugin ) ) {
1307 $this->plugin = CHARITABLE_PLUGIN_BASENAME;
1308 }
1309
1310 if ( $this->plugin === $plugin_file && ! empty( $action_links ) ) {
1311 foreach ( $action_links as $key => $value ) {
1312 $link = array(
1313 $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>',
1314 );
1315
1316 $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
1317 }
1318 }
1319
1320 return $actions;
1321 }
1322
1323 /**
1324 * Set up licensing for pro.
1325 *
1326 * @since 1.7.0
1327 *
1328 * @return void
1329 */
1330 public function setup_licensing() {
1331 charitable_get_helper( 'licenses' )->register_licensed_product(
1332 self::NAME,
1333 self::AUTHOR,
1334 self::VERSION,
1335 __FILE__
1336 );
1337 }
1338
1339 /**
1340 * Deativates and/or warnings about any Charitable verison conflicts.
1341 *
1342 * @since 1.7.0
1343 */
1344 public function check_for_version_conflicts() {
1345
1346 // Check for Charitable Stripe < 1.5.0.
1347 if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1348 if ( class_exists( 'Charitable_Stripe' ) && version_compare( Charitable_Stripe::VERSION, '1.5.0', '<' ) ) { // phpcs:ignore
1349 // manually deactivate plugin.
1350 $found = false;
1351 $current = get_option( 'active_plugins', array() );
1352 $key = array_search( 'charitable-stripe/charitable-stripe.php', $current, true );
1353 if ( false !== $key ) {
1354 $found = true;
1355
1356 unset( $current[ $key ] );
1357 update_option( 'active_plugins', $current );
1358
1359 // translators: %s is the version number.
1360 $message = sprintf( __( '<p><strong style="color:red;">NOTICE:</strong> <strong>Charitable</strong> detected an out-of-date and incompatible version of <strong>Charitable Stripe</strong> addon. To avoid issues with Stripe donations, <strong>Charitable Stripe</strong> %s was deactivated. Please update <strong>Charitable Stripe</strong> to version 1.5.0 or higher.</p><p>You can get the latest version of <strong>Charitable Stripe</strong> through your <a href="https://www.wpcharitable.com/account/" target="_blank">Charitable account at wpcharitable.com</a>. All active licenses are accessible in the "My Downloads" tab.</p>', 'charitable' ), Charitable_Stripe::VERSION );
1361
1362 wp_die( $message . ' <a href="' . admin_url( 'plugins.php' ) . '">Click here to return to the plugins screen.</a>' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1363 }
1364 add_option( 'charitable_version_warning', $message );
1365 } else {
1366 delete_option( 'charitable_version_warning' );
1367 }
1368 }
1369 }
1370
1371 /**
1372 * Set initial timestamps for rotating menu items if they're already installed.
1373 * This ensures the 7-day countdown starts immediately for already-installed plugins.
1374 *
1375 * @since 1.8.8
1376 *
1377 * @return void
1378 */
1379 private function set_initial_rotating_menu_timestamps() {
1380 // Get the rotating menu items configuration
1381 $admin_pages = Charitable_Admin_Pages::get_instance();
1382 $reflection = new ReflectionClass( $admin_pages );
1383 $method = $reflection->getMethod( 'get_marketing_rotation_items' );
1384 $method->setAccessible( true );
1385 $items = $method->invoke( $admin_pages );
1386
1387 $current_time = time();
1388
1389 foreach ( $items as $item ) {
1390 $option_name = $item['option_name'] ?? '';
1391 $option_key = $item['option_key'] ?? '';
1392 $plugin_file = $item['plugin_file'] ?? '';
1393
1394 if ( empty( $option_name ) || empty( $plugin_file ) ) {
1395 continue;
1396 }
1397
1398 // Check if the plugin is installed (file exists in plugins directory)
1399 $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
1400 if ( file_exists( $plugin_path ) ) {
1401 // Plugin is installed, check if we already have a timestamp
1402 $existing_option = get_option( $option_name );
1403 $has_timestamp = false;
1404
1405 if ( is_array( $existing_option ) ) {
1406 $has_timestamp = isset( $existing_option[ $option_key ] ) && is_numeric( $existing_option[ $option_key ] );
1407 } elseif ( is_numeric( $existing_option ) ) {
1408 $has_timestamp = true;
1409 }
1410
1411 // Only set timestamp if one doesn't already exist
1412 if ( ! $has_timestamp ) {
1413 if ( is_array( $existing_option ) ) {
1414 $existing_option[ $option_key ] = $current_time;
1415 update_option( $option_name, $existing_option );
1416 } else {
1417 update_option( $option_name, array( $option_key => $current_time ) );
1418 }
1419 }
1420 }
1421 }
1422 }
1423 }
1424
1425 $charitable = new Charitable();
1426
1427 endif;
1428