PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
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 1.7.0.9 All 211 releases
← All changes | charitable.php +393 -153 1.7.0 → 1.8.13 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
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
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
9 + * Requires at least: 5.9
10 + * Requires PHP: 7.4
11 + * Stable tag: 1.8.13
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';
43 + const VERSION = '1.8.13';
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,58 +227,78 @@
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 . '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().
248 + require_once $includes_path . 'upgrades/charitable-upgrade-hooks.php';
249 + require_once $includes_path . 'users/charitable-user-functions.php';
250 + require_once $includes_path . 'user-management/charitable-user-management-hooks.php';
251 + require_once $includes_path . 'utilities/charitable-utility-functions.php';
252 + require_once $includes_path . 'elementor/charitable-elementor-hooks.php';
208 253
209 - If ( $this->load_core_stripe() ) :
254 + // Load security hooks on both admin and frontend (CAPTCHA needs to work on frontend).
255 + require_once $this->get_path( 'includes', true ) . 'admin/security/charitable-security-hooks.php';
210 256
211 - /* Load Stripe SDK */
212 - require_once( $this->get_path( 'directory' ) . 'vendor/autoload.php' );
257 + /* Load vendor/autoload.php - our modified platform_check.php and autoload_real.php will handle conditional loading */
258 + require_once $this->get_path( 'directory' ) . 'vendor/autoload.php';
213 259
260 + /* Square Compatibility Wrapper - always load this regardless of PHP version */
261 + require_once $includes_path . 'square/class-charitable-square-compatibility.php';
262 +
263 + /* Initialize Square conditionally - will check for compatibility internally */
264 + Charitable_Square_Compatibility::init();
265 +
266 + if ( $this->load_core_stripe() ) :
267 +
214 268 /* Interfaces */
215 - require_once( $includes_path . 'stripe/interfaces/interface-charitable-stripe-gateway-processor.php' );
269 + require_once $includes_path . 'stripe/interfaces/interface-charitable-stripe-gateway-processor.php';
216 270
217 271 /* Abstract classes */
218 - require_once( $includes_path . 'stripe/abstracts/class-charitable-stripe-gateway-processor.php' );
272 + require_once $includes_path . 'stripe/abstracts/class-charitable-stripe-gateway-processor.php';
219 273
220 274 /* 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' );
275 + require_once $includes_path . 'stripe/donations/class-charitable-stripe-donation-log.php';
276 + require_once $includes_path . 'stripe/donations/class-charitable-stripe-recurring-donation-log.php';
277 + require_once $includes_path . 'stripe/i18n/class-charitable-stripe-i18n.php';
278 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-connected-customer.php';
279 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-customer.php';
280 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-payment-intents.php';
281 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-payment-intent.php';
282 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-plan.php';
283 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-product.php';
228 284
285 + require_once $includes_path . 'stripe/connect/charitable-stripe-connect-functions.php';
286 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-gateway-processor-checkout.php';
287 +
229 288 /* Functions & Hooks */
230 - require_once( $includes_path . 'stripe/charitable-stripe-core-functions.php' );
231 - require_once( $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php' );
232 - require_once( $includes_path . 'stripe/gateway/charitable-stripe-gateway-hooks.php' );
289 + require_once $includes_path . 'stripe/charitable-stripe-core-functions.php';
290 + require_once $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php';
291 + require_once $includes_path . 'stripe/gateway/charitable-stripe-gateway-hooks.php';
233 292
234 293 /* webhooks */
235 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php' );
236 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-processor.php' );
294 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php';
295 + require_once $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-processor.php';
237 296
238 297 endif;
239 298
299 + /* Load PayPal Commerce Gateway */
300 + require_once $includes_path . 'gateways/paypal-commerce/charitable-paypal-commerce.php';
240 301 }
241 302
242 303 /**
243 304 * Load the template functions after theme is loaded.
@@ -250,10 +311,10 @@
250 311 */
251 312 public function load_template_files() {
252 313 $includes_path = $this->get_path( 'includes' );
253 314
254 - require_once( $includes_path . 'public/charitable-template-functions.php' );
255 - require_once( $includes_path . 'public/charitable-template-hooks.php' );
315 + require_once $includes_path . 'public/charitable-template-functions.php';
316 + require_once $includes_path . 'public/charitable-template-hooks.php';
256 317 }
257 318
258 319 /**
259 320 * Dynamically loads the class attempting to be instantiated elsewhere in the
@@ -276,21 +337,20 @@
276 337 }
277 338
278 339 /* Autogenerated class map. */
279 340 if ( ! isset( $this->classmap ) ) {
280 - $this->classmap = include( 'includes/autoloader/charitable-class-map.php' );
341 + $this->classmap = include 'includes/autoloader/charitable-class-map.php';
281 342
282 - $this->classmap[ 'Charitable_Gateway_Stripe_AM' ] = 'gateways/class-charitable-gateway-stripe-am.php';
283 - $this->classmap[ 'Charitable_Gateway_Stripe' ] = 'gateways/class-charitable-gateway-stripe-am.php';
343 + $this->classmap['Charitable_Gateway_Stripe_AM'] = 'gateways/class-charitable-gateway-stripe-am.php';
344 + $this->classmap['Charitable_Gateway_Stripe'] = 'gateways/class-charitable-gateway-stripe-am.php';
284 345 }
285 346
286 347 $file_path = isset( $this->classmap[ $class_name ] ) ? $this->get_path( 'includes' ) . $this->classmap[ $class_name ] : false;
287 348
288 349 if ( false !== $file_path && file_exists( $file_path ) && is_file( $file_path ) ) {
289 - require_once( $file_path );
350 + require_once $file_path;
290 351 return true;
291 352 }
292 -
293 353 return false;
294 354 }
295 355
296 356 /**
@@ -313,12 +373,13 @@
313 373 $this->registry->register_object( Charitable_Licenses::get_instance() );
314 374 $this->registry->register_object( Charitable_User_Dashboard::get_instance() );
315 375 $this->registry->register_object( Charitable_Locations::get_instance() );
316 376 $this->registry->register_object( Charitable_Currency::get_instance() );
377 + $this->registry->register_object( Charitable_Notifications::get_instance() );
317 378
318 - $this->registry->register_object( new Charitable_Privacy );
319 - $this->registry->register_object( new Charitable_Debugging );
320 - $this->registry->register_object( new Charitable_Locale );
379 + $this->registry->register_object( new Charitable_Privacy() );
380 + $this->registry->register_object( new Charitable_Debugging() );
381 + $this->registry->register_object( new Charitable_Locale() );
321 382 }
322 383
323 384 return $this->registry;
324 385 }
@@ -334,10 +395,10 @@
334 395 add_action( 'wpmu_new_blog', array( $this, 'maybe_activate_charitable_on_new_site' ) );
335 396 add_action( 'plugins_loaded', array( $this, 'charitable_install' ), 100 );
336 397 add_action( 'plugins_loaded', array( $this, 'charitable_start' ), 100 );
337 398 add_action( 'plugins_loaded', array( $this, 'endpoints' ), 100 );
338 - add_action( 'plugins_loaded', array( $this, 'donation_fields' ), 100 );
339 - add_action( 'plugins_loaded', array( $this, 'campaign_fields' ), 100 );
399 + add_action( 'init', array( $this, 'donation_fields' ), 100 );
400 + add_action( 'init', array( $this, 'campaign_fields' ), 100 );
340 401 add_action( 'plugins_loaded', array( $this, 'register_donormeta_table' ) );
341 402 add_action( 'plugins_loaded', 'charitable_load_compat_functions' );
342 403 add_action( 'setup_theme', array( 'Charitable_Customizer', 'start' ) );
343 404 add_action( 'after_setup_theme', array( $this, 'load_template_files' ) );
@@ -349,19 +410,17 @@
349 410 * as addons) has a chance to run before the event.
350 411 */
351 412 add_action( 'init', array( $this, 'do_charitable_actions' ), 20 );
352 413
353 - If ( $this->load_core_stripe() ) :
414 + if ( $this->load_core_stripe() ) :
354 415
355 416 /**
356 417 * Set up scripts & stylesheets & enqueue them when appropriate.
357 418 */
358 - // add_action( 'wp_enqueue_scripts', array( $this, 'setup_stripe_scripts' ), 11 );
359 419 add_action( 'charitable_form_after_fields', array( $this, 'maybe_setup_stripe_scripts_in_donation_form' ) );
360 420 add_action( 'charitable_campaign_loop_after', array( $this, 'maybe_setup_stripe_scripts_in_campaign_loop' ) );
361 421
362 422 endif;
363 -
364 423 }
365 424
366 425
367 426 /**
@@ -431,25 +490,70 @@
431 490 if ( ! is_admin() ) {
432 491 return false;
433 492 }
434 493
435 - require_once( $this->get_path( 'admin' ) . 'class-charitable-admin.php' );
436 - require_once( $this->get_path( 'admin' ) . 'charitable-admin-hooks.php' );
494 + // load blocks/Gutenberg related.
495 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
437 496
438 - $admin = Charitable_Admin::get_instance();
497 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin.php';
498 + require_once $this->get_path( 'admin' ) . 'charitable-admin-hooks.php';
439 499
440 - $this->registry->register_object( $admin );
500 + // load campaign builder.
501 + require_once $this->get_path( 'admin' ) . 'campaign-builder/util.php';
502 + require_once $this->get_path( 'admin' ) . 'campaign-builder/functions.php';
503 + require_once $this->get_path( 'admin' ) . 'campaign-builder/ajax-actions.php';
504 + require_once $this->get_path( 'admin' ) . 'campaign-builder/functions-legacy.php';
505 + require_once $this->get_path( 'admin' ) . 'campaign-builder/debug.php';
506 + require_once $this->get_path( 'admin' ) . 'campaign-builder/access.php';
507 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
508 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
441 509
442 - // Stripe
510 + // connect for upgrade.wpcharitable.com.
511 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
443 512
444 - require_once( $this->get_path( 'includes' ) . 'stripe/admin/class-charitable-stripe-admin.php' );
513 + // misc.
514 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-pointers.php';
515 + require_once $this->get_path( 'admin' ) . 'plugins/charitable-admin-plugin-hooks.php';
516 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-getting-started.php';
445 517
446 - new Charitable_Stripe_Admin;
518 + // Site Analysis / Recommendations tab.
519 + require_once $this->get_path( 'admin' ) . 'site-analysis/class-charitable-site-analysis.php';
520 + Charitable_Site_Analysis::get_instance();
447 521
522 + $admin = Charitable_Admin::get_instance();
523 + $this->registry->register_object( $admin );
524 +
525 + // Stripe.
526 + require_once $this->get_path( 'includes' ) . 'stripe/admin/class-charitable-stripe-admin.php';
527 + new Charitable_Stripe_Admin();
528 +
448 529 return $admin;
449 530 }
450 531
451 532 /**
533 + * Run the upgrade-server connect callback on init before any redirect (e.g. to login) can run.
534 + * Only registered when CHARITABLE_CONNECT_CALLBACK is defined (request came from connect-callback.php).
535 + *
536 + * @since 1.8.9.6
537 + */
538 + public function run_connect_callback() {
539 + ini_set( 'display_errors', '0' );
540 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
541 + require_once ABSPATH . 'wp-admin/includes/file.php';
542 + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
543 + $connect_file = $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
544 + if ( file_exists( $connect_file ) ) {
545 + require_once $connect_file;
546 + }
547 + if ( ! class_exists( 'Charitable_Admin_Connect' ) ) {
548 + header( 'Content-Type: application/json' );
549 + wp_send_json( array( 'success' => false, 'data' => array( 'message' => 'Charitable not loaded.' ) ) );
550 + }
551 + Charitable_Admin_Connect::get_instance()->process();
552 + exit;
553 + }
554 +
555 + /**
452 556 * Checks whether we're on the public-facing side and if so, loads the public-facing functionality.
453 557 *
454 558 * @since 1.0.0
455 559 *
@@ -459,10 +563,18 @@
459 563 if ( is_admin() && ! $this->is_ajax() ) {
460 564 return false;
461 565 }
462 566
463 - require_once( $this->get_path( 'public' ) . 'class-charitable-public.php' );
567 + require_once $this->get_path( 'public' ) . 'class-charitable-public.php';
464 568
569 + // Campaign Builder related (v1.8.0).
570 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-campaign-builder-preview.php';
571 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-fields.php';
572 + require_once $this->get_path( 'admin' ) . 'campaign-builder/class-builder-form-fields.php';
573 +
574 + // load blocks/Gutenberg related.
575 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-blocks.php';
576 +
465 577 $public = Charitable_Public::get_instance();
466 578
467 579 $this->registry->register_object( $public );
468 580
@@ -477,25 +589,51 @@
477 589 * @return boolean
478 590 */
479 591 public function maybe_start_qunit() {
480 592 /* Skip out early if ?qunit isn't included in the request. */
481 - if ( ! array_key_exists( 'qunit', $_GET ) ) {
593 + if ( ! array_key_exists( 'qunit', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
482 594 return false;
483 595 }
484 596
485 - /* The unit tests have to exist. */
486 - if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
487 - return false;
488 - }
597 + /* The unit tests have to exist. */
598 + if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
599 + return false;
600 + }
489 601
490 - wp_register_script( 'qunit', 'https://code.jquery.com/qunit/qunit-2.3.3.js', array(), '2.3.3', true );
602 + /**
603 + * Register QUnit testing library.
604 + *
605 + * Note: These files must be bundled locally for WordPress.org compliance.
606 + * Download from:
607 + * - JS: https://code.jquery.com/qunit/qunit-2.3.3.js
608 + * - CSS: https://code.jquery.com/qunit/qunit-2.3.3.css
609 + * Save to:
610 + * - assets/js/libraries/qunit-2.3.3.js
611 + * - assets/css/libraries/qunit-2.3.3.css
612 + *
613 + * @since 1.0.0
614 + * @version 1.8.9.1
615 + */
616 + wp_register_script(
617 + 'qunit',
618 + $this->get_path( 'directory', false ) . 'assets/js/libraries/qunit-2.3.3.js',
619 + array(),
620 + '2.3.3',
621 + true
622 + );
491 623
492 - /* Version: '20170615-15:44' */
493 - wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
494 - wp_enqueue_script( 'qunit-tests' );
624 + /* Version: '20170615-15:44' */
625 + wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
626 + wp_enqueue_script( 'qunit-tests' );
495 627
496 - wp_register_style( 'qunit', 'https://code.jquery.com/qunit/qunit-2.3.3.css', array(), '2.3.3', 'all' );
497 - wp_enqueue_style( 'qunit' );
628 + wp_register_style(
629 + 'qunit',
630 + $this->get_path( 'directory', false ) . 'assets/css/libraries/qunit-2.3.3.css',
631 + array(),
632 + '2.3.3',
633 + 'all'
634 + );
635 + wp_enqueue_style( 'qunit' );
498 636 }
499 637
500 638 /**
501 639 * Checks whether the current request is an AJAX request.
@@ -519,10 +657,10 @@
519 657 if ( ! $this->is_ajax() ) {
520 658 return;
521 659 }
522 660
523 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php' );
524 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-hooks.php' );
661 + require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php';
662 + require_once $this->get_path( 'includes' ) . 'ajax/charitable-ajax-hooks.php';
525 663 }
526 664
527 665 /**
528 666 * This method is fired after all plugins are loaded and simply fires the charitable_start hook.
@@ -540,9 +678,11 @@
540 678 /**
541 679 * Fires off an action right after Charitable is installed, allowing other
542 680 * plugins/themes to do something at this point.
543 681 *
544 - * @since 1.0.1
682 + * @since 1.0.1
683 + * @version 1.8.1.12 - Added transient for activation redirect.
684 + * @version 1.8.3 - Added call to add version and delete notifications.
545 685 *
546 686 * @return void
547 687 */
548 688 public function charitable_install() {
@@ -551,9 +691,9 @@
551 691 if ( ! $install ) {
552 692 return;
553 693 }
554 694
555 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
695 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
556 696
557 697 Charitable_Install::finish_installing();
558 698
559 699 do_action( 'charitable_install' );
@@ -558,8 +698,28 @@
558 698
559 699 do_action( 'charitable_install' );
560 700
561 701 delete_transient( 'charitable_install' );
702 +
703 + // Store the date when the initial activation was performed.
704 + $type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'pro' : 'lite';
705 + $activated = (array) get_option( 'charitable_activated', array() );
706 +
707 + // If no activation date is set, assume it's a new install and set the activation redirect transient.
708 + if ( false === $activated || empty( $activated ) ) {
709 + // Add transient to trigger redirect to the Welcome / Getting Started screen.
710 + set_transient( 'charitable_activation_redirect', true, 30 );
711 + }
712 +
713 + $this->add_version_to_upgraded_from();
714 +
715 + if ( empty( $activated[ $type ] ) ) {
716 + $activated[ $type ] = time();
717 + update_option( 'charitable_activated', $activated );
718 + }
719 +
720 + // Clear notifications.
721 + delete_option( 'charitable_notifications' );
562 722 }
563 723
564 724 /**
565 725 * Returns whether we are currently in the start phase of the plugin.
@@ -568,9 +728,9 @@
568 728 *
569 729 * @return boolean
570 730 */
571 731 public function is_start() {
572 - return current_filter() == 'charitable_start';
732 + return current_filter() == 'charitable_start'; // phpcs:ignore
573 733 }
574 734
575 735 /**
576 736 * Returns whether the plugin has already started.
@@ -579,9 +739,9 @@
579 739 *
580 740 * @return boolean
581 741 */
582 742 public function started() {
583 - return did_action( 'charitable_start' ) || current_filter() == 'charitable_start';
743 + return did_action( 'charitable_start' ) || current_filter() == 'charitable_start'; // phpcs:ignore
584 744 }
585 745
586 746 /**
587 747 * Returns whether the plugin is being activated.
@@ -590,9 +750,9 @@
590 750 *
591 751 * @return boolean
592 752 */
593 753 public function is_activation() {
594 - return current_filter() == 'activate_charitable/charitable.php';
754 + return current_filter() == 'activate_charitable/charitable.php'; // phpcs:ignore
595 755 }
596 756
597 757 /**
598 758 * Returns whether the plugin is being deactivated.
@@ -601,9 +761,9 @@
601 761 *
602 762 * @return boolean
603 763 */
604 764 public function is_deactivation() {
605 - return current_filter() == 'deactivate_charitable/charitable.php';
765 + return current_filter() == 'deactivate_charitable/charitable.php'; // phpcs:ignore
606 766 }
607 767
608 768 /**
609 769 * Returns plugin paths.
@@ -637,8 +797,12 @@
637 797 case 'templates':
638 798 $path = $base . 'templates/';
639 799 break;
640 800
801 + case 'plugin-directory':
802 + $path = WP_PLUGIN_DIR;
803 + break;
804 +
641 805 case 'directory':
642 806 $path = $base;
643 807 break;
644 808
@@ -668,8 +832,26 @@
668 832 return $version;
669 833 }
670 834
671 835 /**
836 + * Adds previous versions to the upgraded_from option.
837 + *
838 + * @since 1.8.3
839 + */
840 + public function add_version_to_upgraded_from() {
841 +
842 + $upgraded_from = get_option( 'charitable_version_upgraded_from', array() );
843 +
844 + if ( ! is_array( $upgraded_from ) ) {
845 + $upgraded_from = array();
846 + }
847 +
848 + $upgraded_from[] = $this->get_version();
849 +
850 + update_option( 'charitable_version_upgraded_from', $upgraded_from, false );
851 + }
852 +
853 + /**
672 854 * Get the campaign fields registry
673 855 *
674 856 * If the registry has not been set up yet, this will also
675 857 * perform the task of setting it up initially.
@@ -686,9 +868,9 @@
686 868
687 869 /* Register it immediately to avoid endless recursion. */
688 870 $this->registry->register_object( $campaign_fields );
689 871
690 - $fields = include( $this->get_path( 'includes' ) . 'fields/default-fields/campaign-fields.php' );
872 + $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/campaign-fields.php';
691 873
692 874 foreach ( $fields as $key => $args ) {
693 875 $campaign_fields->register_field( new Charitable_Campaign_Field( $key, $args ) );
694 876 }
@@ -715,9 +897,9 @@
715 897
716 898 /* Register it immediately to avoid endless recursion. */
717 899 $this->registry->register_object( $donation_fields );
718 900
719 - $fields = include( $this->get_path( 'includes' ) . 'fields/default-fields/donation-fields.php' );
901 + $fields = include $this->get_path( 'includes' ) . 'fields/default-fields/donation-fields.php';
720 902
721 903 foreach ( $fields as $key => $args ) {
722 904 $donation_fields->register_field( new Charitable_Donation_Field( $key, $args ) );
723 905 }
@@ -749,22 +931,22 @@
749 931 * BEFORE the endpoint it builds on. In other words, move from
750 932 * most specific to least specific.
751 933 */
752 934 $endpoints = new Charitable_Endpoints();
753 - $endpoints->register( new Charitable_Campaign_Donation_Endpoint );
754 - $endpoints->register( new Charitable_Campaign_Widget_Endpoint );
755 - $endpoints->register( new Charitable_Campaign_Endpoint );
756 - $endpoints->register( new Charitable_Donation_Cancellation_Endpoint );
757 - $endpoints->register( new Charitable_Donation_Processing_Endpoint );
758 - $endpoints->register( new Charitable_Donation_Receipt_Endpoint );
759 - $endpoints->register( new Charitable_Email_Preview_Endpoint );
760 - $endpoints->register( new Charitable_Email_Verification_Endpoint );
761 - $endpoints->register( new Charitable_Forgot_Password_Endpoint );
762 - $endpoints->register( new Charitable_Reset_Password_Endpoint );
763 - $endpoints->register( new Charitable_Registration_Endpoint );
764 - $endpoints->register( new Charitable_Login_Endpoint );
765 - $endpoints->register( new Charitable_Profile_Endpoint );
766 - $endpoints->register( new Charitable_Webhook_Listener_Endpoint );
935 + $endpoints->register( new Charitable_Campaign_Donation_Endpoint() );
936 + $endpoints->register( new Charitable_Campaign_Widget_Endpoint() );
937 + $endpoints->register( new Charitable_Campaign_Endpoint() );
938 + $endpoints->register( new Charitable_Donation_Cancellation_Endpoint() );
939 + $endpoints->register( new Charitable_Donation_Processing_Endpoint() );
940 + $endpoints->register( new Charitable_Donation_Receipt_Endpoint() );
941 + $endpoints->register( new Charitable_Email_Preview_Endpoint() );
942 + $endpoints->register( new Charitable_Email_Verification_Endpoint() );
943 + $endpoints->register( new Charitable_Forgot_Password_Endpoint() );
944 + $endpoints->register( new Charitable_Reset_Password_Endpoint() );
945 + $endpoints->register( new Charitable_Registration_Endpoint() );
946 + $endpoints->register( new Charitable_Login_Endpoint() );
947 + $endpoints->register( new Charitable_Profile_Endpoint() );
948 + $endpoints->register( new Charitable_Webhook_Listener_Endpoint() );
767 949
768 950 $this->registry->register_object( $endpoints );
769 951 }
770 952
@@ -778,9 +960,9 @@
778 960 *
779 961 * @param string $class The type of class to be retrieved.
780 962 * @return object
781 963 */
782 - public function get_registered_object( $class ) {
964 + public function get_registered_object( $class ) { // phpcs:ignore
783 965 return $this->registry->get( $class );
784 966 }
785 967
786 968 /**
@@ -831,8 +1013,9 @@
831 1013 /**
832 1014 * Filter the array of available Charitable table classes.
833 1015 *
834 1016 * @since 1.0.0
1017 + * @version 1.8.1
835 1018 *
836 1019 * @param array $tables List of tables as a key=>value array.
837 1020 */
838 1021 return apply_filters(
@@ -837,11 +1020,13 @@
837 1020 */
838 1021 return apply_filters(
839 1022 'charitable_db_tables',
840 1023 array(
841 - 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
842 - 'donors' => 'Charitable_Donors_DB',
843 - 'donormeta' => 'Charitable_Donormeta_DB',
1024 + 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
1025 + 'donors' => 'Charitable_Donors_DB',
1026 + 'donormeta' => 'Charitable_Donormeta_DB',
1027 + 'donation_activities' => 'Charitable_Donation_Activities_DB',
1028 + 'campaign_activities' => 'Charitable_Campaign_Activities_DB',
844 1029 )
845 1030 );
846 1031 }
847 1032
@@ -872,14 +1057,14 @@
872 1057 * or just the current site. Multisite only. Default is false.
873 1058 * @return void
874 1059 */
875 1060 public function activate( $network_wide = false ) {
876 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
1061 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
877 1062
878 1063 if ( is_multisite() && $network_wide ) {
879 1064 global $wpdb;
880 1065
881 - foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) {
1066 + foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) { // phpcs:ignore
882 1067 switch_to_blog( $blog_id );
883 1068 new Charitable_Install( $this->includes_path );
884 1069 restore_current_blog();
885 1070 }
@@ -887,9 +1072,12 @@
887 1072 new Charitable_Install( $this->includes_path );
888 1073 }
889 1074
890 1075 // create or update the install date for future reference.
891 - update_option( 'wpcharitable_activated_datetime', current_time( 'timestamp' ) );
1076 + update_option( 'wpcharitable_activated_datetime', current_time( 'timestamp' ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1077 +
1078 + // Set initial timestamps for rotating menu items if they're already installed
1079 + $this->set_initial_rotating_menu_timestamps();
892 1080 }
893 1081
894 1082 /**
895 1083 * Runs on plugin deactivation.
@@ -900,9 +1088,9 @@
900 1088 *
901 1089 * @return void
902 1090 */
903 1091 public function deactivate() {
904 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php' );
1092 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php';
905 1093 new Charitable_Uninstall();
906 1094 }
907 1095
908 1096 /**
@@ -912,11 +1100,11 @@
912 1100 *
913 1101 * @return void
914 1102 */
915 1103 public function do_charitable_actions() {
916 - if ( isset( $_REQUEST['charitable_action'] ) ) {
1104 + if ( isset( $_REQUEST['charitable_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
917 1105
918 - $action = $_REQUEST['charitable_action'];
1106 + $action = esc_attr( $_REQUEST['charitable_action'] ); // phpcs:ignore
919 1107
920 1108 /**
921 1109 * Handle Charitable action.
922 1110 *
@@ -992,9 +1180,9 @@
992 1180 *
993 1181 * @param mixed $object Object to be registered.
994 1182 * @return void
995 1183 */
996 - public function register_object( $object ) {
1184 + public function register_object( $object ) { // phpcs:ignore
997 1185 charitable_get_deprecated()->deprecated_function(
998 1186 __METHOD__,
999 1187 '1.5.0',
1000 1188 'charitable()->registry()->register_object()'
@@ -1099,18 +1287,13 @@
1099 1287 if ( false !== ( defined( 'USE_NEW_STRIPE' ) && USE_NEW_STRIPE ) ) {
1100 1288 return true;
1101 1289 }
1102 1290
1103 - // check for stripe addon
1291 + // 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.
1292 + 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() ) ) {
1293 + return false;
1294 + }
1104 1295
1105 - // if ( in_array('charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option('active_plugins' ) ) ) ) {
1106 - // return false;
1107 - // }
1108 -
1109 - // if ( class_exists( 'Charitable_Stripe' ) ) {
1110 - // return false;
1111 - // }
1112 -
1113 1296 return true;
1114 1297 }
1115 1298
1116 1299 /**
@@ -1121,9 +1304,9 @@
1121 1304 * @return boolean
1122 1305 */
1123 1306 public function is_stripe_connect_addon() {
1124 1307
1125 - if ( in_array('charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option('active_plugins' ) ) ) ) {
1308 + if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1126 1309 return true;
1127 1310 }
1128 1311
1129 1312 return false;
@@ -1133,10 +1316,11 @@
1133 1316 * Action links for the plugins page.
1134 1317 *
1135 1318 * @since 1.7.0
1136 1319 *
1137 - * @param array $actions An array of existing actions.
1138 - * @param string $plugin_file The plugin file we are modifying.
1320 + * @param array $actions An array of existing actions.
1321 + * @param string $plugin_file The plugin file we are modifying.
1322 + * @param boolean $action_links An array of action links to add.
1139 1323 * @return array An array of action links.
1140 1324 */
1141 1325 public function plugin_action_links( $actions, $plugin_file, $action_links = false ) {
1142 1326
@@ -1143,15 +1327,15 @@
1143 1327 if ( ! charitable_is_pro() ) :
1144 1328
1145 1329 // if this isn't pro, add the upgrade link which is visible on the plugins page until the plugin name.
1146 1330
1147 - $action_links = [
1148 - 'proupgrade' => [
1331 + $action_links = array(
1332 + 'proupgrade' => array(
1149 1333 // Translators: This is an action link users can click to purchase a license for All in One SEO Pro.
1150 1334 'label' => __( 'Upgrade to Pro', 'charitable' ),
1151 1335 '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',
1152 - ]
1153 - ];
1336 + ),
1337 + );
1154 1338
1155 1339 endif;
1156 1340
1157 1341 if ( isset( $actions['edit'] ) ) {
@@ -1165,23 +1349,24 @@
1165 1349 * Parse the action links.
1166 1350 *
1167 1351 * @since 1.7.0
1168 1352 *
1169 - * @param array $actions
1170 - * @param string $pluginFile
1171 - * @param
1353 + * @param array $actions An array of existing actions.
1354 + * @param string $plugin_file The plugin file we are modifying.
1355 + * @param array $action_links An array of action links to add.
1356 + * @param string $position The position to add the action links.
1172 1357 * @return array
1173 1358 */
1174 - protected function parse_action_links( $actions, $pluginFile, $action_links = [], $position = 'after' ) {
1359 + protected function parse_action_links( $actions, $plugin_file, $action_links = array(), $position = 'after' ) {
1175 1360 if ( empty( $this->plugin ) ) {
1176 - $this->plugin = CHARTIABLE_PLUGIN_BASENAME;
1361 + $this->plugin = CHARITABLE_PLUGIN_BASENAME;
1177 1362 }
1178 1363
1179 - if ( $this->plugin === $pluginFile && ! empty( $action_links ) ) {
1364 + if ( $this->plugin === $plugin_file && ! empty( $action_links ) ) {
1180 1365 foreach ( $action_links as $key => $value ) {
1181 - $link = [
1182 - $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>'
1183 - ];
1366 + $link = array(
1367 + $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>',
1368 + );
1184 1369
1185 1370 $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
1186 1371 }
1187 1372 }
@@ -1197,11 +1382,11 @@
1197 1382 * @return void
1198 1383 */
1199 1384 public function setup_licensing() {
1200 1385 charitable_get_helper( 'licenses' )->register_licensed_product(
1201 - Charitable::NAME . ' Pro',
1202 - Charitable::AUTHOR,
1203 - Charitable::VERSION,
1386 + self::NAME,
1387 + self::AUTHOR,
1388 + self::VERSION,
1204 1389 __FILE__
1205 1390 );
1206 1391 }
1207 1392
@@ -1208,16 +1393,14 @@
1208 1393 /**
1209 1394 * Deativates and/or warnings about any Charitable verison conflicts.
1210 1395 *
1211 1396 * @since 1.7.0
1212 - *
1213 - * @return null
1214 1397 */
1215 1398 public function check_for_version_conflicts() {
1216 1399
1217 - // Check for Charitable Stripe < 1.5.0
1218 - if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
1219 - if ( version_compare( Charitable_Stripe::VERSION, '1.5.0', '<' ) ) {
1400 + // Check for Charitable Stripe < 1.5.0.
1401 + if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { // phpcs:ignore
1402 + if ( class_exists( 'Charitable_Stripe' ) && version_compare( Charitable_Stripe::VERSION, '1.5.0', '<' ) ) { // phpcs:ignore
1220 1403 // manually deactivate plugin.
1221 1404 $found = false;
1222 1405 $current = get_option( 'active_plugins', array() );
1223 1406 $key = array_search( 'charitable-stripe/charitable-stripe.php', $current, true );
@@ -1226,21 +1409,78 @@
1226 1409
1227 1410 unset( $current[ $key ] );
1228 1411 update_option( 'active_plugins', $current );
1229 1412
1230 - $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' );
1413 + // translators: %s is the version number.
1414 + $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 );
1231 1415
1232 - wp_die( $message . ' <a href="' . admin_url( 'plugins.php' ) . '">Click here to return to the plugins screen.</a>' );
1416 + wp_die( $message . ' <a href="' . admin_url( 'plugins.php' ) . '">Click here to return to the plugins screen.</a>' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1233 1417 }
1234 - // charitable_get_notices()->add_error( $message );
1235 - add_option( 'chartiable_version_warning', $message );
1418 + add_option( 'charitable_version_warning', $message );
1236 1419 } else {
1237 - delete_option( 'chartiable_version_warning' );
1420 + delete_option( 'charitable_version_warning' );
1238 1421 }
1239 1422 }
1240 1423 }
1241 1424
1425 + /**
1426 + * Set initial timestamps for rotating menu items if they're already installed.
1427 + * This ensures the 7-day countdown starts immediately for already-installed plugins.
1428 + *
1429 + * @since 1.8.8
1430 + *
1431 + * @return void
1432 + */
1433 + private function set_initial_rotating_menu_timestamps() {
1434 + // Get the rotating menu items configuration
1435 + $admin_pages = Charitable_Admin_Pages::get_instance();
1242 1436
1437 + try {
1438 + $reflection = new ReflectionClass( $admin_pages );
1439 + $method = $reflection->getMethod( 'get_marketing_rotation_items' );
1440 + $method->setAccessible( true );
1441 + $items = $method->invoke( $admin_pages );
1442 + } catch ( ReflectionException $e ) {
1443 + // Fallback if reflection fails in future PHP versions
1444 + $items = array();
1445 + }
1446 +
1447 + $current_time = time();
1448 +
1449 + foreach ( $items as $item ) {
1450 + $option_name = $item['option_name'] ?? '';
1451 + $option_key = $item['option_key'] ?? '';
1452 + $plugin_file = $item['plugin_file'] ?? '';
1453 +
1454 + if ( empty( $option_name ) || empty( $plugin_file ) ) {
1455 + continue;
1456 + }
1457 +
1458 + // Check if the plugin is installed (file exists in plugins directory)
1459 + $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
1460 + if ( file_exists( $plugin_path ) ) {
1461 + // Plugin is installed, check if we already have a timestamp
1462 + $existing_option = get_option( $option_name );
1463 + $has_timestamp = false;
1464 +
1465 + if ( is_array( $existing_option ) ) {
1466 + $has_timestamp = isset( $existing_option[ $option_key ] ) && is_numeric( $existing_option[ $option_key ] );
1467 + } elseif ( is_numeric( $existing_option ) ) {
1468 + $has_timestamp = true;
1469 + }
1470 +
1471 + // Only set timestamp if one doesn't already exist
1472 + if ( ! $has_timestamp ) {
1473 + if ( is_array( $existing_option ) ) {
1474 + $existing_option[ $option_key ] = $current_time;
1475 + update_option( $option_name, $existing_option );
1476 + } else {
1477 + update_option( $option_name, array( $option_key => $current_time ) );
1478 + }
1479 + }
1480 + }
1481 + }
1482 + }
1243 1483 }
1244 1484
1245 1485 $charitable = new Charitable();
1246 1486