PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13.1
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13.1
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
← All changes | charitable.php +393 -155 1.7.0.1 → 1.8.13.1 View file →
@@ -1,15 +1,17 @@
1 1 <?php
2 2 /**
3 3 * Plugin Name: Charitable
4 4 * Plugin URI: https://www.wpcharitable.com
5 - * Description: The WordPress fundraising alternative for non-profits, created to help non-profits raise money on their own website.
6 - * Version: 1.7.0.1
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.13.1
7 7 * Author: Charitable Donations & Fundraising Team
8 8 * Author URI: https://wpcharitable.com
9 - * Requires at least: 4.1
10 - * Tested up to: 6.0.2
11 - * Stable tag: 1.7.0.1
9 + * Requires at least: 5.9
10 + * Requires PHP: 7.4
11 + * Stable tag: 1.8.13.1
12 + * License: GPLv2 or later
13 + * License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12 14 *
13 15 * Text Domain: charitable
14 16 * Domain Path: /i18n/languages/
15 17 *
@@ -14,9 +16,9 @@
14 16 * Domain Path: /i18n/languages/
15 17 *
16 18 * @package Charitable
17 19 * @author David Bisset
18 - * @copyright Copyright (c) 2022, WP Charitable LLC
20 + * @copyright Copyright (c) 2026, WP Charitable LLC
19 21 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
20 22 */
21 23
22 24 // Exit if accessed directly.
@@ -37,9 +39,9 @@
37 39 /** The plugin author name. */
38 40 const AUTHOR = 'WP Charitable';
39 41
40 42 /* Plugin version. */
41 - const VERSION = '1.7.0.1';
43 + const VERSION = '1.8.13.1';
42 44
43 45 /* Version of database schema. */
44 46 const DB_VERSION = '20180522';
45 47
@@ -104,8 +106,17 @@
104 106 */
105 107 private $classmap;
106 108
107 109 /**
110 + * Plugin.
111 + *
112 + * @since 1.8.0
113 + *
114 + * @var array
115 + */
116 + private $plugin;
117 +
118 + /**
108 119 * Create class instance.
109 120 *
110 121 * @since 1.0.0
111 122 */
@@ -113,12 +124,26 @@
113 124 $this->directory_path = plugin_dir_path( __FILE__ );
114 125 $this->directory_url = plugin_dir_url( __FILE__ );
115 126 $this->includes_path = $this->directory_path . 'includes/';
116 127
117 - define( "CHARTIABLE_DIRECTORY_PATH", plugin_dir_path( __FILE__ ) );
118 - define( "CHARTIABLE_PLUGIN_BASENAME", plugin_basename( __FILE__ ) );
119 - define( "CHARTIABLE_MARKETING_URL", 'https://wpcharitable.com/' );
128 + define( 'CHARITABLE_DIRECTORY_PATH', plugin_dir_path( __FILE__ ) );
129 + define( 'CHARITABLE_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
130 + define( 'CHARITABLE_MARKETING_URL', 'https://wpcharitable.com/' );
131 + if ( ! defined( 'CHARITABLE_ANALYSIS_API_URL' ) ) {
132 + define( 'CHARITABLE_ANALYSIS_API_URL', apply_filters( 'charitable_analysis_api_url', 'https://nonprofitscore.org/api/v1/charitable/recommendations' ) );
133 + }
134 + // No credential is shipped. Each site earns a runtime-minted, quota-limited per-site token from
135 + // the register endpoint (see Charitable_Site_Analysis::register()); the token is not in plugin source.
136 + if ( ! defined( 'CHARITABLE_ANALYSIS_REGISTER_URL' ) ) {
137 + define( 'CHARITABLE_ANALYSIS_REGISTER_URL', apply_filters( 'charitable_analysis_register_url', 'https://nonprofitscore.org/api/v1/charitable/register' ) );
138 + }
139 + // Note: CHARITABLE_ANALYSIS_SALT is intentionally NOT defined here. It would need wp_salt(),
140 + // which lives in pluggable.php and is not loaded yet at plugin-construct time. The salt is
141 + // resolved lazily in Charitable_Site_Analysis::gather_config() (admin-ajax time), and a host
142 + // may still pre-define the CHARITABLE_ANALYSIS_SALT constant (e.g. in wp-config.php).
120 143
144 + define( 'CHARITABLE_BUILDER_SHOW_LEGACY_EDIT_LINKS', true ); // 1.8.0 specific.
145 +
121 146 spl_autoload_register( array( $this, 'autoloader' ) );
122 147
123 148 $this->load_dependencies();
124 149
@@ -124,12 +149,28 @@
124 149
125 150 register_activation_hook( __FILE__, array( $this, 'activate' ) );
126 151 register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
127 152
128 - add_filter( 'plugin_action_links_' . CHARTIABLE_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ), 99, 2 );
153 + add_filter( 'plugin_action_links_' . CHARITABLE_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ), 99, 2 );
129 154 add_action( 'plugins_loaded', array( $this, 'start' ), 3 );
130 155
131 156 add_action( 'plugins_loaded', array( $this, 'check_for_version_conflicts' ), 2 );
157 +
158 + // Run upgrade-server callback before any redirect (e.g. login) can run. Request from connect-callback.php.
159 + if ( defined( 'CHARITABLE_CONNECT_CALLBACK' ) && CHARITABLE_CONNECT_CALLBACK ) {
160 + add_action( 'init', array( $this, 'run_connect_callback' ), -9999 );
161 + }
162 +
163 + // Initialize error handler early.
164 + add_action(
165 + 'plugins_loaded',
166 + function () {
167 + require_once $this->get_path( 'includes' ) . 'class-charitable-error-handler.php';
168 + $error_handler = new Charitable_Error_Handler();
169 + $error_handler->init();
170 + },
171 + 1
172 + );
132 173 }
133 174
134 175 /**
135 176 * Returns the original instance of this class.
@@ -186,61 +227,79 @@
186 227 private function load_dependencies() {
187 228 $includes_path = $this->get_path( 'includes' );
188 229
189 230 /* Load files with hooks & functions. Classes are autoloaded. */
190 - require_once( $includes_path . 'charitable-core-functions.php' );
191 - require_once( $includes_path . 'api/charitable-api-functions.php' );
192 - require_once( $includes_path . 'campaigns/charitable-campaign-functions.php' );
193 - require_once( $includes_path . 'campaigns/charitable-campaign-hooks.php' );
194 - require_once( $includes_path . 'compat/charitable-compat-functions.php' );
195 - require_once( $includes_path . 'currency/charitable-currency-functions.php' );
196 - require_once( $includes_path . 'deprecated/charitable-deprecated-functions.php' );
197 - require_once( $includes_path . 'donations/charitable-donation-hooks.php' );
198 - require_once( $includes_path . 'donations/charitable-donation-functions.php' );
199 - require_once( $includes_path . 'emails/charitable-email-hooks.php' );
200 - require_once( $includes_path . 'endpoints/charitable-endpoints-functions.php' );
201 - require_once( $includes_path . 'privacy/charitable-privacy-functions.php' );
202 - require_once( $includes_path . 'public/charitable-template-helpers.php' );
203 - require_once( $includes_path . 'shortcodes/charitable-shortcodes-hooks.php' );
204 - require_once( $includes_path . 'upgrades/charitable-upgrade-hooks.php' );
205 - require_once( $includes_path . 'users/charitable-user-functions.php' );
206 - require_once( $includes_path . 'user-management/charitable-user-management-hooks.php' );
207 - require_once( $includes_path . 'utilities/charitable-utility-functions.php' );
231 + require_once $includes_path . 'class-charitable-log-muter.php';
232 + require_once $includes_path . 'charitable-core-functions.php';
233 + require_once $includes_path . 'api/charitable-api-functions.php';
234 + require_once $includes_path . 'campaigns/charitable-campaign-functions.php';
235 + require_once $includes_path . 'campaigns/charitable-campaign-hooks.php';
236 + require_once $includes_path . 'compat/charitable-compat-functions.php';
237 + require_once $includes_path . 'currency/charitable-currency-functions.php';
238 + require_once $includes_path . 'deprecated/charitable-deprecated-functions.php';
239 + require_once $includes_path . 'donations/charitable-donation-hooks.php';
240 + require_once $includes_path . 'donations/charitable-donation-functions.php';
241 + require_once $includes_path . 'emails/charitable-email-hooks.php';
242 + require_once $includes_path . 'endpoints/charitable-endpoints-functions.php';
243 + require_once $includes_path . 'privacy/charitable-privacy-functions.php';
244 + require_once $includes_path . 'public/charitable-template-helpers.php';
245 + require_once $includes_path . 'shortcodes/charitable-shortcodes-hooks.php';
246 + require_once $includes_path . 'tracking/charitable-tracking-hooks.php'; // 1.8.12 - MUST be outside the admin gate; see the file header.
247 + require_once $includes_path . 'addons-directory/charitable-addons-catalogue-hooks.php'; // 1.8.13.1 - MUST be outside the admin gate; see the file header.
248 + require_once $includes_path . 'abilities/charitable-abilities-hooks.php'; // 1.8.13 - MUST be outside the admin gate: abilities register on REST and WP-CLI requests, neither of which is is_admin().
249 + require_once $includes_path . 'upgrades/charitable-upgrade-hooks.php';
250 + require_once $includes_path . 'users/charitable-user-functions.php';
251 + require_once $includes_path . 'user-management/charitable-user-management-hooks.php';
252 + require_once $includes_path . 'utilities/charitable-utility-functions.php';
253 + require_once $includes_path . 'elementor/charitable-elementor-hooks.php';
208 254
209 - If ( $this->load_core_stripe() ) :
255 + // Load security hooks on both admin and frontend (CAPTCHA needs to work on frontend).
256 + require_once $this->get_path( 'includes', true ) . 'admin/security/charitable-security-hooks.php';
210 257
211 - /* Load Stripe SDK */
212 - require_once( $this->get_path( 'directory' ) . 'vendor/autoload.php' );
258 + /* Load vendor/autoload.php - our modified platform_check.php and autoload_real.php will handle conditional loading */
259 + require_once $this->get_path( 'directory' ) . 'vendor/autoload.php';
213 260
261 + /* Square Compatibility Wrapper - always load this regardless of PHP version */
262 + require_once $includes_path . 'square/class-charitable-square-compatibility.php';
263 +
264 + /* Initialize Square conditionally - will check for compatibility internally */
265 + Charitable_Square_Compatibility::init();
266 +
267 + if ( $this->load_core_stripe() ) :
268 +
214 269 /* Interfaces */
215 - require_once( $includes_path . 'stripe/interfaces/interface-charitable-stripe-gateway-processor.php' );
270 + require_once $includes_path . 'stripe/interfaces/interface-charitable-stripe-gateway-processor.php';
216 271
217 272 /* Abstract classes */
218 - require_once( $includes_path . 'stripe/abstracts/class-charitable-stripe-gateway-processor.php' );
273 + require_once $includes_path . 'stripe/abstracts/class-charitable-stripe-gateway-processor.php';
219 274
220 275 /* Classes */
221 - require_once( $includes_path . 'stripe/donations/class-charitable-stripe-donation-log.php' );
222 - require_once( $includes_path . 'stripe/donations/class-charitable-stripe-recurring-donation-log.php' );
223 - require_once( $includes_path . 'stripe/i18n/class-charitable-stripe-i18n.php' );
224 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-connected-customer.php' );
225 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-customer.php' );
226 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-payment-intents.php' );
227 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-payment-intent.php' );
276 + require_once $includes_path . 'stripe/donations/class-charitable-stripe-donation-log.php';
277 + require_once $includes_path . 'stripe/donations/class-charitable-stripe-recurring-donation-log.php';
278 + require_once $includes_path . 'stripe/i18n/class-charitable-stripe-i18n.php';
279 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-connected-customer.php';
280 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-customer.php';
281 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-payment-intents.php';
282 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-payment-intent.php';
283 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-plan.php';
284 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-product.php';
228 285
229 - require_once( $includes_path . 'stripe/connect/charitable-stripe-connect-functions.php' );
230 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-checkout.php' );
286 + require_once $includes_path . 'stripe/connect/charitable-stripe-connect-functions.php';
287 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-checkout.php';
231 288
232 289 /* Functions & Hooks */
233 - require_once( $includes_path . 'stripe/charitable-stripe-core-functions.php' );
234 - require_once( $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php' );
235 - require_once( $includes_path . 'stripe/gateway/charitable-stripe-gateway-hooks.php' );
290 + require_once $includes_path . 'stripe/charitable-stripe-core-functions.php';
291 + require_once $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php';
292 + require_once $includes_path . 'stripe/gateway/charitable-stripe-gateway-hooks.php';
236 293
237 294 /* webhooks */
238 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php' );
239 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-processor.php' );
295 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php';
296 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-processor.php';
240 297
241 298 endif;
242 299
300 + /* Load PayPal Commerce Gateway */
301 + require_once $includes_path . 'gateways/paypal-commerce/charitable-paypal-commerce.php';
243 302 }
244 303
245 304 /**
246 305 * Load the template functions after theme is loaded.
@@ -253,10 +312,10 @@
253 312 */
254 313 public function load_template_files() {
255 314 $includes_path = $this->get_path( 'includes' );
256 315
257 - require_once( $includes_path . 'public/charitable-template-functions.php' );
258 - require_once( $includes_path . 'public/charitable-template-hooks.php' );
316 + require_once $includes_path . 'public/charitable-template-functions.php';
317 + require_once $includes_path . 'public/charitable-template-hooks.php';
259 318 }
260 319
261 320 /**
262 321 * Dynamically loads the class attempting to be instantiated elsewhere in the
@@ -279,21 +338,20 @@
279 338 }
280 339
281 340 /* Autogenerated class map. */
282 341 if ( ! isset( $this->classmap ) ) {
283 - $this->classmap = include( 'includes/autoloader/charitable-class-map.php' );
342 + $this->classmap = include 'includes/autoloader/charitable-class-map.php';
284 343
285 - $this->classmap[ 'Charitable_Gateway_Stripe_AM' ] = 'gateways/class-charitable-gateway-stripe-am.php';
286 - $this->classmap[ 'Charitable_Gateway_Stripe' ] = 'gateways/class-charitable-gateway-stripe-am.php';
344 + $this->classmap['Charitable_Gateway_Stripe_AM'] = 'gateways/class-charitable-gateway-stripe-am.php';
345 + $this->classmap['Charitable_Gateway_Stripe'] = 'gateways/class-charitable-gateway-stripe-am.php';
287 346 }
288 347
289 348 $file_path = isset( $this->classmap[ $class_name ] ) ? $this->get_path( 'includes' ) . $this->classmap[ $class_name ] : false;
290 349
291 350 if ( false !== $file_path && file_exists( $file_path ) && is_file( $file_path ) ) {
292 - require_once( $file_path );
351 + require_once $file_path;
293 352 return true;
294 353 }
295 -
296 354 return false;
297 355 }
298 356
299 357 /**
@@ -316,12 +374,13 @@
316 374 $this->registry->register_object( Charitable_Licenses::get_instance() );
317 375 $this->registry->register_object( Charitable_User_Dashboard::get_instance() );
318 376 $this->registry->register_object( Charitable_Locations::get_instance() );
319 377 $this->registry->register_object( Charitable_Currency::get_instance() );
378 + $this->registry->register_object( Charitable_Notifications::get_instance() );
320 379
321 - $this->registry->register_object( new Charitable_Privacy );
322 - $this->registry->register_object( new Charitable_Debugging );
323 - $this->registry->register_object( new Charitable_Locale );
380 + $this->registry->register_object( new Charitable_Privacy() );
381 + $this->registry->register_object( new Charitable_Debugging() );
382 + $this->registry->register_object( new Charitable_Locale() );
324 383 }
325 384
326 385 return $this->registry;
327 386 }
@@ -337,10 +396,10 @@
337 396 add_action( 'wpmu_new_blog', array( $this, 'maybe_activate_charitable_on_new_site' ) );
338 397 add_action( 'plugins_loaded', array( $this, 'charitable_install' ), 100 );
339 398 add_action( 'plugins_loaded', array( $this, 'charitable_start' ), 100 );
340 399 add_action( 'plugins_loaded', array( $this, 'endpoints' ), 100 );
341 - add_action( 'plugins_loaded', array( $this, 'donation_fields' ), 100 );
342 - add_action( 'plugins_loaded', array( $this, 'campaign_fields' ), 100 );
400 + add_action( 'init', array( $this, 'donation_fields' ), 100 );
401 + add_action( 'init', array( $this, 'campaign_fields' ), 100 );
343 402 add_action( 'plugins_loaded', array( $this, 'register_donormeta_table' ) );
344 403 add_action( 'plugins_loaded', 'charitable_load_compat_functions' );
345 404 add_action( 'setup_theme', array( 'Charitable_Customizer', 'start' ) );
346 405 add_action( 'after_setup_theme', array( $this, 'load_template_files' ) );
@@ -352,19 +411,17 @@
352 411 * as addons) has a chance to run before the event.
353 412 */
354 413 add_action( 'init', array( $this, 'do_charitable_actions' ), 20 );
355 414
356 - If ( $this->load_core_stripe() ) :
415 + if ( $this->load_core_stripe() ) :
357 416
358 417 /**
359 418 * Set up scripts & stylesheets & enqueue them when appropriate.
360 419 */
361 - // add_action( 'wp_enqueue_scripts', array( $this, 'setup_stripe_scripts' ), 11 );
362 420 add_action( 'charitable_form_after_fields', array( $this, 'maybe_setup_stripe_scripts_in_donation_form' ) );
363 421 add_action( 'charitable_campaign_loop_after', array( $this, 'maybe_setup_stripe_scripts_in_campaign_loop' ) );
364 422
365 423 endif;
366 -
367 424 }
368 425
369 426
370 427 /**
@@ -434,25 +491,70 @@
434 491 if ( ! is_admin() ) {
435 492 return false;
436 493 }
437 494
438 - require_once( $this->get_path( 'admin' ) . 'class-charitable-admin.php' );
439 - require_once( $this->get_path( 'admin' ) . 'charitable-admin-hooks.php' );
495 + // load blocks/Gutenberg related.
496 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
440 497
441 - $admin = Charitable_Admin::get_instance();
498 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin.php';
499 + require_once $this->get_path( 'admin' ) . 'charitable-admin-hooks.php';
442 500
443 - $this->registry->register_object( $admin );
501 + // load campaign builder.
502 + require_once $this->get_path( 'admin' ) . 'campaign-builder/util.php';
503 + require_once $this->get_path( 'admin' ) . 'campaign-builder/functions.php';
504 + require_once $this->get_path( 'admin' ) . 'campaign-builder/ajax-actions.php';
505 + require_once $this->get_path( 'admin' ) . 'campaign-builder/functions-legacy.php';
506 + require_once $this->get_path( 'admin' ) . 'campaign-builder/debug.php';
507 + require_once $this->get_path( 'admin' ) . 'campaign-builder/access.php';
508 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
509 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
444 510
445 - // Stripe
511 + // connect for upgrade.wpcharitable.com.
512 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
446 513
447 - require_once( $this->get_path( 'includes' ) . 'stripe/admin/class-charitable-stripe-admin.php' );
514 + // misc.
515 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-pointers.php';
516 + require_once $this->get_path( 'admin' ) . 'plugins/charitable-admin-plugin-hooks.php';
517 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-getting-started.php';
448 518
449 - new Charitable_Stripe_Admin;
519 + // Site Analysis / Recommendations tab.
520 + require_once $this->get_path( 'admin' ) . 'site-analysis/class-charitable-site-analysis.php';
521 + Charitable_Site_Analysis::get_instance();
450 522
523 + $admin = Charitable_Admin::get_instance();
524 + $this->registry->register_object( $admin );
525 +
526 + // Stripe.
527 + require_once $this->get_path( 'includes' ) . 'stripe/admin/class-charitable-stripe-admin.php';
528 + new Charitable_Stripe_Admin();
529 +
451 530 return $admin;
452 531 }
453 532
454 533 /**
534 + * Run the upgrade-server connect callback on init before any redirect (e.g. to login) can run.
535 + * Only registered when CHARITABLE_CONNECT_CALLBACK is defined (request came from connect-callback.php).
536 + *
537 + * @since 1.8.9.6
538 + */
539 + public function run_connect_callback() {
540 + ini_set( 'display_errors', '0' );
541 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
542 + require_once ABSPATH . 'wp-admin/includes/file.php';
543 + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
544 + $connect_file = $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
545 + if ( file_exists( $connect_file ) ) {
546 + require_once $connect_file;
547 + }
548 + if ( ! class_exists( 'Charitable_Admin_Connect' ) ) {
549 + header( 'Content-Type: application/json' );
550 + wp_send_json( array( 'success' => false, 'data' => array( 'message' => 'Charitable not loaded.' ) ) );
551 + }
552 + Charitable_Admin_Connect::get_instance()->process();
553 + exit;
554 + }
555 +
556 + /**
455 557 * Checks whether we're on the public-facing side and if so, loads the public-facing functionality.
456 558 *
457 559 * @since 1.0.0
458 560 *
@@ -462,10 +564,18 @@
462 564 if ( is_admin() && ! $this->is_ajax() ) {
463 565 return false;
464 566 }
465 567
466 - require_once( $this->get_path( 'public' ) . 'class-charitable-public.php' );
568 + require_once $this->get_path( 'public' ) . 'class-charitable-public.php';
467 569
570 + // Campaign Builder related (v1.8.0).
571 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-campaign-builder-preview.php';
572 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
573 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
574 +
575 + // load blocks/Gutenberg related.
576 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
577 +
468 578 $public = Charitable_Public::get_instance();
469 579
470 580 $this->registry->register_object( $public );
471 581
@@ -480,25 +590,51 @@
480 590 * @return boolean
481 591 */
482 592 public function maybe_start_qunit() {
483 593 /* Skip out early if ?qunit isn't included in the request. */
484 - if ( ! array_key_exists( 'qunit', $_GET ) ) {
594 + if ( ! array_key_exists( 'qunit', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
485 595 return false;
486 596 }
487 597
488 - /* The unit tests have to exist. */
489 - if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
490 - return false;
491 - }
598 + /* The unit tests have to exist. */
599 + if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
600 + return false;
601 + }
492 602
493 - wp_register_script( 'qunit', 'https://code.jquery.com/qunit/qunit-2.3.3.js', array(), '2.3.3', true );
603 + /**
604 + * Register QUnit testing library.
605 + *
606 + * Note: These files must be bundled locally for WordPress.org compliance.
607 + * Download from:
608 + * - JS: https://code.jquery.com/qunit/qunit-2.3.3.js
609 + * - CSS: https://code.jquery.com/qunit/qunit-2.3.3.css
610 + * Save to:
611 + * - assets/js/libraries/qunit-2.3.3.js
612 + * - assets/css/libraries/qunit-2.3.3.css
613 + *
614 + * @since 1.0.0
615 + * @version 1.8.9.1
616 + */
617 + wp_register_script(
618 + 'qunit',
619 + $this->get_path( 'directory', false ) . 'assets/js/libraries/qunit-2.3.3.js',
620 + array(),
621 + '2.3.3',
622 + true
623 + );
494 624
495 - /* Version: '20170615-15:44' */
496 - wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
497 - wp_enqueue_script( 'qunit-tests' );
625 + /* Version: '20170615-15:44' */
626 + wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
627 + wp_enqueue_script( 'qunit-tests' );
498 628
499 - wp_register_style( 'qunit', 'https://code.jquery.com/qunit/qunit-2.3.3.css', array(), '2.3.3', 'all' );
500 - wp_enqueue_style( 'qunit' );
629 + wp_register_style(
630 + 'qunit',
631 + $this->get_path( 'directory', false ) . 'assets/css/libraries/qunit-2.3.3.css',
632 + array(),
633 + '2.3.3',
634 + 'all'
635 + );
636 + wp_enqueue_style( 'qunit' );
501 637 }
502 638
503 639 /**
504 640 * Checks whether the current request is an AJAX request.
@@ -522,10 +658,10 @@
522 658 if ( ! $this->is_ajax() ) {
523 659 return;
524 660 }
525 661
526 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php' );
527 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-hooks.php' );
662 + require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php';
663 + require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-hooks.php';
528 664 }
529 665
530 666 /**
531 667 * This method is fired after all plugins are loaded and simply fires the charitable_start hook.
@@ -543,9 +679,11 @@
543 679 /**
544 680 * Fires off an action right after Charitable is installed, allowing other
545 681 * plugins/themes to do something at this point.
546 682 *
547 - * @since 1.0.1
683 + * @since 1.0.1
684 + * @version 1.8.1.12 - Added transient for activation redirect.
685 + * @version 1.8.3 - Added call to add version and delete notifications.
548 686 *
549 687 * @return void
550 688 */
551 689 public function charitable_install() {
@@ -554,9 +692,9 @@
554 692 if ( ! $install ) {
555 693 return;
556 694 }
557 695
558 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
696 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
559 697
560 698 Charitable_Install::finish_installing();
561 699
562 700 do_action( 'charitable_install' );
@@ -561,8 +699,28 @@
561 699
562 700 do_action( 'charitable_install' );
563 701
564 702 delete_transient( 'charitable_install' );
703 +
704 + // Store the date when the initial activation was performed.
705 + $type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'pro' : 'lite';
706 + $activated = (array) get_option( 'charitable_activated', array() );
707 +
708 + // If no activation date is set, assume it's a new install and set the activation redirect transient.
709 + if ( false === $activated || empty( $activated ) ) {
710 + // Add transient to trigger redirect to the Welcome / Getting Started screen.
711 + set_transient( 'charitable_activation_redirect', true, 30 );
712 + }
713 +
714 + $this->add_version_to_upgraded_from();
715 +
716 + if ( empty( $activated[ $type ] ) ) {
717 + $activated[ $type ] = time();
718 + update_option( 'charitable_activated', $activated );
719 + }
720 +
721 + // Clear notifications.
722 + delete_option( 'charitable_notifications' );
565 723 }
566 724
567 725 /**
568 726 * Returns whether we are currently in the start phase of the plugin.
@@ -571,9 +729,9 @@
571 729 *
572 730 * @return boolean
573 731 */
574 732 public function is_start() {
575 - return current_filter() == 'charitable_start';
733 + return current_filter() == 'charitable_start'; // phpcs:ignore
576 734 }
577 735
578 736 /**
579 737 * Returns whether the plugin has already started.
@@ -582,9 +740,9 @@
582 740 *
583 741 * @return boolean
584 742 */
585 743 public function started() {
586 - return did_action( 'charitable_start' ) || current_filter() == 'charitable_start';
744 + return did_action( 'charitable_start' ) || current_filter() == 'charitable_start'; // phpcs:ignore
587 745 }
588 746
589 747 /**
590 748 * Returns whether the plugin is being activated.
@@ -593,9 +751,9 @@
593 751 *
594 752 * @return boolean
595 753 */
596 754 public function is_activation() {
597 - return current_filter() == 'activate_charitable/charitable.php';
755 + return current_filter() == 'activate_charitable/charitable.php'; // phpcs:ignore
598 756 }
599 757
600 758 /**
601 759 * Returns whether the plugin is being deactivated.
@@ -604,9 +762,9 @@
604 762 *
605 763 * @return boolean
606 764 */
607 765 public function is_deactivation() {
608 - return current_filter() == 'deactivate_charitable/charitable.php';
766 + return current_filter() == 'deactivate_charitable/charitable.php'; // phpcs:ignore
609 767 }
610 768
611 769 /**
612 770 * Returns plugin paths.
@@ -640,8 +798,12 @@
640 798 case 'templates':
641 799 $path = $base . 'templates/';
642 800 break;
643 801
802 + case 'plugin-directory':
803 + $path = WP_PLUGIN_DIR;
804 + break;
805 +
644 806 case 'directory':
645 807 $path = $base;
646 808 break;
647 809
@@ -671,8 +833,26 @@
671 833 return $version;
672 834 }
673 835
674 836 /**
837 + * Adds previous versions to the upgraded_from option.
838 + *
839 + * @since 1.8.3
840 + */
841 + public function add_version_to_upgraded_from() {
842 +
843 + $upgraded_from = get_option( 'charitable_version_upgraded_from', array() );
844 +
845 + if ( ! is_array( $upgraded_from ) ) {
846 + $upgraded_from = array();
847 + }
848 +
849 + $upgraded_from[] = $this->get_version();
850 +
851 + update_option( 'charitable_version_upgraded_from', $upgraded_from, false );
852 + }
853 +
854 + /**
675 855 * Get the campaign fields registry
676 856 *
677 857 * If the registry has not been set up yet, this will also
678 858 * perform the task of setting it up initially.
@@ -689,9 +869,9 @@
689 869
690 870 /* Register it immediately to avoid endless recursion. */
691 871 $this->registry->register_object( $campaign_fields );
692 872
693 - $fields = include( $this->get_path( 'includes' ) . 'fields/default-fields/campaign-fields.php' );
873 + $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/campaign-fields.php';
694 874
695 875 foreach ( $fields as $key => $args ) {
696 876 $campaign_fields->register_field( new Charitable_Campaign_Field( $key, $args ) );
697 877 }
@@ -718,9 +898,9 @@
718 898
719 899 /* Register it immediately to avoid endless recursion. */
720 900 $this->registry->register_object( $donation_fields );
721 901
722 - $fields = include( $this->get_path( 'includes' ) . 'fields/default-fields/donation-fields.php' );
902 + $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/donation-fields.php';
723 903
724 904 foreach ( $fields as $key => $args ) {
725 905 $donation_fields->register_field( new Charitable_Donation_Field( $key, $args ) );
726 906 }
@@ -752,22 +932,22 @@
752 932 * BEFORE the endpoint it builds on. In other words, move from
753 933 * most specific to least specific.
754 934 */
755 935 $endpoints = new Charitable_Endpoints();
756 - $endpoints->register( new Charitable_Campaign_Donation_Endpoint );
757 - $endpoints->register( new Charitable_Campaign_Widget_Endpoint );
758 - $endpoints->register( new Charitable_Campaign_Endpoint );
759 - $endpoints->register( new Charitable_Donation_Cancellation_Endpoint );
760 - $endpoints->register( new Charitable_Donation_Processing_Endpoint );
761 - $endpoints->register( new Charitable_Donation_Receipt_Endpoint );
762 - $endpoints->register( new Charitable_Email_Preview_Endpoint );
763 - $endpoints->register( new Charitable_Email_Verification_Endpoint );
764 - $endpoints->register( new Charitable_Forgot_Password_Endpoint );
765 - $endpoints->register( new Charitable_Reset_Password_Endpoint );
766 - $endpoints->register( new Charitable_Registration_Endpoint );
767 - $endpoints->register( new Charitable_Login_Endpoint );
768 - $endpoints->register( new Charitable_Profile_Endpoint );
769 - $endpoints->register( new Charitable_Webhook_Listener_Endpoint );
936 + $endpoints->register( new Charitable_Campaign_Donation_Endpoint() );
937 + $endpoints->register( new Charitable_Campaign_Widget_Endpoint() );
938 + $endpoints->register( new Charitable_Campaign_Endpoint() );
939 + $endpoints->register( new Charitable_Donation_Cancellation_Endpoint() );
940 + $endpoints->register( new Charitable_Donation_Processing_Endpoint() );
941 + $endpoints->register( new Charitable_Donation_Receipt_Endpoint() );
942 + $endpoints->register( new Charitable_Email_Preview_Endpoint() );
943 + $endpoints->register( new Charitable_Email_Verification_Endpoint() );
944 + $endpoints->register( new Charitable_Forgot_Password_Endpoint() );
945 + $endpoints->register( new Charitable_Reset_Password_Endpoint() );
946 + $endpoints->register( new Charitable_Registration_Endpoint() );
947 + $endpoints->register( new Charitable_Login_Endpoint() );
948 + $endpoints->register( new Charitable_Profile_Endpoint() );
949 + $endpoints->register( new Charitable_Webhook_Listener_Endpoint() );
770 950
771 951 $this->registry->register_object( $endpoints );
772 952 }
773 953
@@ -781,9 +961,9 @@
781 961 *
782 962 * @param string $class The type of class to be retrieved.
783 963 * @return object
784 964 */
785 - public function get_registered_object( $class ) {
965 + public function get_registered_object( $class ) { // phpcs:ignore
786 966 return $this->registry->get( $class );
787 967 }
788 968
789 969 /**
@@ -834,8 +1014,9 @@
834 1014 /**
835 1015 * Filter the array of available Charitable table classes.
836 1016 *
837 1017 * @since 1.0.0
1018 + * @version 1.8.1
838 1019 *
839 1020 * @param array $tables List of tables as a key=>value array.
840 1021 */
841 1022 return apply_filters(
@@ -840,11 +1021,13 @@
840 1021 */
841 1022 return apply_filters(
842 1023 'charitable_db_tables',
843 1024 array(
844 - 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
845 - 'donors' => 'Charitable_Donors_DB',
846 - 'donormeta' => 'Charitable_Donormeta_DB',
1025 + 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
1026 + 'donors' => 'Charitable_Donors_DB',
1027 + 'donormeta' => 'Charitable_Donormeta_DB',
1028 + 'donation_activities' => 'Charitable_Donation_Activities_DB',
1029 + 'campaign_activities' => 'Charitable_Campaign_Activities_DB',
847 1030 )
848 1031 );
849 1032 }
850 1033
@@ -875,14 +1058,14 @@
875 1058 * or just the current site. Multisite only. Default is false.
876 1059 * @return void
877 1060 */
878 1061 public function activate( $network_wide = false ) {
879 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
1062 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
880 1063
881 1064 if ( is_multisite() && $network_wide ) {
882 1065 global $wpdb;
883 1066
884 - foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) {
1067 + foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) { // phpcs:ignore
885 1068 switch_to_blog( $blog_id );
886 1069 new Charitable_Install( $this->includes_path );
887 1070 restore_current_blog();
888 1071 }
@@ -890,9 +1073,12 @@
890 1073 new Charitable_Install( $this->includes_path );
891 1074 }
892 1075
893 1076 // create or update the install date for future reference.
894 - update_option( 'wpcharitable_activated_datetime', current_time( 'timestamp' ) );
1077 + update_option( 'wpcharitable_activated_datetime', current_time( 'timestamp' ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1078 +
1079 + // Set initial timestamps for rotating menu items if they're already installed
1080 + $this->set_initial_rotating_menu_timestamps();
895 1081 }
896 1082
897 1083 /**
898 1084 * Runs on plugin deactivation.
@@ -903,9 +1089,9 @@
903 1089 *
904 1090 * @return void
905 1091 */
906 1092 public function deactivate() {
907 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php' );
1093 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php';
908 1094 new Charitable_Uninstall();
909 1095 }
910 1096
911 1097 /**
@@ -915,11 +1101,11 @@
915 1101 *
916 1102 * @return void
917 1103 */
918 1104 public function do_charitable_actions() {
919 - if ( isset( $_REQUEST['charitable_action'] ) ) {
1105 + if ( isset( $_REQUEST['charitable_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
920 1106
921 - $action = $_REQUEST['charitable_action'];
1107 + $action = esc_attr( $_REQUEST['charitable_action'] ); // phpcs:ignore
922 1108
923 1109 /**
924 1110 * Handle Charitable action.
925 1111 *
@@ -995,9 +1181,9 @@
995 1181 *
996 1182 * @param mixed $object Object to be registered.
997 1183 * @return void
998 1184 */
999 - public function register_object( $object ) {
1185 + public function register_object( $object ) { // phpcs:ignore
1000 1186 charitable_get_deprecated()->deprecated_function(
1001 1187 __METHOD__,
1002 1188 '1.5.0',
1003 1189 'charitable()->registry()->register_object()'
@@ -1102,18 +1288,13 @@
1102 1288 if ( false !== ( defined( 'USE_NEW_STRIPE' ) && USE_NEW_STRIPE ) ) {
1103 1289 return true;
1104 1290 }
1105 1291
1106 - // check for stripe addon
1292 + // 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.
1293 + 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() ) ) {
1294 + return false;
1295 + }
1107 1296
1108 - // if ( in_array('charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option('active_plugins' ) ) ) ) {
1109 - // return false;
1110 - // }
1111 -
1112 - // if ( class_exists( 'Charitable_Stripe' ) ) {
1113 - // return false;
1114 - // }
1115 -
1116 1297 return true;
1117 1298 }
1118 1299
1119 1300 /**
@@ -1124,9 +1305,9 @@
1124 1305 * @return boolean
1125 1306 */
1126 1307 public function is_stripe_connect_addon() {
1127 1308
1128 - if ( in_array('charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option('active_plugins' ) ) ) ) {
1309 + if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1129 1310 return true;
1130 1311 }
1131 1312
1132 1313 return false;
@@ -1136,10 +1317,11 @@
1136 1317 * Action links for the plugins page.
1137 1318 *
1138 1319 * @since 1.7.0
1139 1320 *
1140 - * @param array $actions An array of existing actions.
1141 - * @param string $plugin_file The plugin file we are modifying.
1321 + * @param array $actions An array of existing actions.
1322 + * @param string $plugin_file The plugin file we are modifying.
1323 + * @param boolean $action_links An array of action links to add.
1142 1324 * @return array An array of action links.
1143 1325 */
1144 1326 public function plugin_action_links( $actions, $plugin_file, $action_links = false ) {
1145 1327
@@ -1146,15 +1328,15 @@
1146 1328 if ( ! charitable_is_pro() ) :
1147 1329
1148 1330 // if this isn't pro, add the upgrade link which is visible on the plugins page until the plugin name.
1149 1331
1150 - $action_links = [
1151 - 'proupgrade' => [
1332 + $action_links = array(
1333 + 'proupgrade' => array(
1152 1334 // Translators: This is an action link users can click to purchase a license for All in One SEO Pro.
1153 1335 'label' => __( 'Upgrade to Pro', 'charitable' ),
1154 1336 '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',
1155 - ]
1156 - ];
1337 + ),
1338 + );
1157 1339
1158 1340 endif;
1159 1341
1160 1342 if ( isset( $actions['edit'] ) ) {
@@ -1168,23 +1350,24 @@
1168 1350 * Parse the action links.
1169 1351 *
1170 1352 * @since 1.7.0
1171 1353 *
1172 - * @param array $actions
1173 - * @param string $pluginFile
1174 - * @param
1354 + * @param array $actions An array of existing actions.
1355 + * @param string $plugin_file The plugin file we are modifying.
1356 + * @param array $action_links An array of action links to add.
1357 + * @param string $position The position to add the action links.
1175 1358 * @return array
1176 1359 */
1177 - protected function parse_action_links( $actions, $pluginFile, $action_links = [], $position = 'after' ) {
1360 + protected function parse_action_links( $actions, $plugin_file, $action_links = array(), $position = 'after' ) {
1178 1361 if ( empty( $this->plugin ) ) {
1179 - $this->plugin = CHARTIABLE_PLUGIN_BASENAME;
1362 + $this->plugin = CHARITABLE_PLUGIN_BASENAME;
1180 1363 }
1181 1364
1182 - if ( $this->plugin === $pluginFile && ! empty( $action_links ) ) {
1365 + if ( $this->plugin === $plugin_file && ! empty( $action_links ) ) {
1183 1366 foreach ( $action_links as $key => $value ) {
1184 - $link = [
1185 - $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>'
1186 - ];
1367 + $link = array(
1368 + $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>',
1369 + );
1187 1370
1188 1371 $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
1189 1372 }
1190 1373 }
@@ -1200,11 +1383,11 @@
1200 1383 * @return void
1201 1384 */
1202 1385 public function setup_licensing() {
1203 1386 charitable_get_helper( 'licenses' )->register_licensed_product(
1204 - Charitable::NAME . ' Pro',
1205 - Charitable::AUTHOR,
1206 - Charitable::VERSION,
1387 + self::NAME,
1388 + self::AUTHOR,
1389 + self::VERSION,
1207 1390 __FILE__
1208 1391 );
1209 1392 }
1210 1393
@@ -1211,16 +1394,14 @@
1211 1394 /**
1212 1395 * Deativates and/or warnings about any Charitable verison conflicts.
1213 1396 *
1214 1397 * @since 1.7.0
1215 - *
1216 - * @return null
1217 1398 */
1218 1399 public function check_for_version_conflicts() {
1219 1400
1220 - // Check for Charitable Stripe < 1.5.0
1221 - if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
1222 - if ( version_compare( Charitable_Stripe::VERSION, '1.5.0', '<' ) ) {
1401 + // Check for Charitable Stripe < 1.5.0.
1402 + if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1403 + if ( class_exists( 'Charitable_Stripe' ) && version_compare( Charitable_Stripe::VERSION, '1.5.0', '<' ) ) { // phpcs:ignore
1223 1404 // manually deactivate plugin.
1224 1405 $found = false;
1225 1406 $current = get_option( 'active_plugins', array() );
1226 1407 $key = array_search( 'charitable-stripe/charitable-stripe.php', $current, true );
@@ -1229,21 +1410,78 @@
1229 1410
1230 1411 unset( $current[ $key ] );
1231 1412 update_option( 'active_plugins', $current );
1232 1413
1233 - $message = __( '<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> ' . Charitable_Stripe::VERSION . ' 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-stripe' );
1414 + // translators: %s is the version number.
1415 + $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 );
1234 1416
1235 - wp_die( $message . ' <a href="' . admin_url( 'plugins.php' ) . '">Click here to return to the plugins screen.</a>' );
1417 + wp_die( $message . ' <a href="' . admin_url( 'plugins.php' ) . '">Click here to return to the plugins screen.</a>' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1236 1418 }
1237 - // charitable_get_notices()->add_error( $message );
1238 - add_option( 'chartiable_version_warning', $message );
1419 + add_option( 'charitable_version_warning', $message );
1239 1420 } else {
1240 - delete_option( 'chartiable_version_warning' );
1421 + delete_option( 'charitable_version_warning' );
1241 1422 }
1242 1423 }
1243 1424 }
1244 1425
1426 + /**
1427 + * Set initial timestamps for rotating menu items if they're already installed.
1428 + * This ensures the 7-day countdown starts immediately for already-installed plugins.
1429 + *
1430 + * @since 1.8.8
1431 + *
1432 + * @return void
1433 + */
1434 + private function set_initial_rotating_menu_timestamps() {
1435 + // Get the rotating menu items configuration
1436 + $admin_pages = Charitable_Admin_Pages::get_instance();
1245 1437
1438 + try {
1439 + $reflection = new ReflectionClass( $admin_pages );
1440 + $method = $reflection->getMethod( 'get_marketing_rotation_items' );
1441 + $method->setAccessible( true );
1442 + $items = $method->invoke( $admin_pages );
1443 + } catch ( ReflectionException $e ) {
1444 + // Fallback if reflection fails in future PHP versions
1445 + $items = array();
1446 + }
1447 +
1448 + $current_time = time();
1449 +
1450 + foreach ( $items as $item ) {
1451 + $option_name = $item['option_name'] ?? '';
1452 + $option_key = $item['option_key'] ?? '';
1453 + $plugin_file = $item['plugin_file'] ?? '';
1454 +
1455 + if ( empty( $option_name ) || empty( $plugin_file ) ) {
1456 + continue;
1457 + }
1458 +
1459 + // Check if the plugin is installed (file exists in plugins directory)
1460 + $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
1461 + if ( file_exists( $plugin_path ) ) {
1462 + // Plugin is installed, check if we already have a timestamp
1463 + $existing_option = get_option( $option_name );
1464 + $has_timestamp = false;
1465 +
1466 + if ( is_array( $existing_option ) ) {
1467 + $has_timestamp = isset( $existing_option[ $option_key ] ) && is_numeric( $existing_option[ $option_key ] );
1468 + } elseif ( is_numeric( $existing_option ) ) {
1469 + $has_timestamp = true;
1470 + }
1471 +
1472 + // Only set timestamp if one doesn't already exist
1473 + if ( ! $has_timestamp ) {
1474 + if ( is_array( $existing_option ) ) {
1475 + $existing_option[ $option_key ] = $current_time;
1476 + update_option( $option_name, $existing_option );
1477 + } else {
1478 + update_option( $option_name, array( $option_key => $current_time ) );
1479 + }
1480 + }
1481 + }
1482 + }
1483 + }
1246 1484 }
1247 1485
1248 1486 $charitable = new Charitable();
1249 1487