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 +392 -155 1.7.0.1 → 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.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
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
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';
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,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
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' );
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';
231 287
232 288 /* 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' );
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';
236 292
237 293 /* 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' );
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';
240 296
241 297 endif;
242 298
299 + /* Load PayPal Commerce Gateway */
300 + require_once $includes_path . 'gateways/paypal-commerce/charitable-paypal-commerce.php';
243 301 }
244 302
245 303 /**
246 304 * Load the template functions after theme is loaded.
@@ -253,10 +311,10 @@
253 311 */
254 312 public function load_template_files() {
255 313 $includes_path = $this->get_path( 'includes' );
256 314
257 - require_once( $includes_path . 'public/charitable-template-functions.php' );
258 - 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';
259 317 }
260 318
261 319 /**
262 320 * Dynamically loads the class attempting to be instantiated elsewhere in the
@@ -279,21 +337,20 @@
279 337 }
280 338
281 339 /* Autogenerated class map. */
282 340 if ( ! isset( $this->classmap ) ) {
283 - $this->classmap = include( 'includes/autoloader/charitable-class-map.php' );
341 + $this->classmap = include 'includes/autoloader/charitable-class-map.php';
284 342
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';
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';
287 345 }
288 346
289 347 $file_path = isset( $this->classmap[ $class_name ] ) ? $this->get_path( 'includes' ) . $this->classmap[ $class_name ] : false;
290 348
291 349 if ( false !== $file_path && file_exists( $file_path ) && is_file( $file_path ) ) {
292 - require_once( $file_path );
350 + require_once $file_path;
293 351 return true;
294 352 }
295 -
296 353 return false;
297 354 }
298 355
299 356 /**
@@ -316,12 +373,13 @@
316 373 $this->registry->register_object( Charitable_Licenses::get_instance() );
317 374 $this->registry->register_object( Charitable_User_Dashboard::get_instance() );
318 375 $this->registry->register_object( Charitable_Locations::get_instance() );
319 376 $this->registry->register_object( Charitable_Currency::get_instance() );
377 + $this->registry->register_object( Charitable_Notifications::get_instance() );
320 378
321 - $this->registry->register_object( new Charitable_Privacy );
322 - $this->registry->register_object( new Charitable_Debugging );
323 - $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() );
324 382 }
325 383
326 384 return $this->registry;
327 385 }
@@ -337,10 +395,10 @@
337 395 add_action( 'wpmu_new_blog', array( $this, 'maybe_activate_charitable_on_new_site' ) );
338 396 add_action( 'plugins_loaded', array( $this, 'charitable_install' ), 100 );
339 397 add_action( 'plugins_loaded', array( $this, 'charitable_start' ), 100 );
340 398 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 );
399 + add_action( 'init', array( $this, 'donation_fields' ), 100 );
400 + add_action( 'init', array( $this, 'campaign_fields' ), 100 );
343 401 add_action( 'plugins_loaded', array( $this, 'register_donormeta_table' ) );
344 402 add_action( 'plugins_loaded', 'charitable_load_compat_functions' );
345 403 add_action( 'setup_theme', array( 'Charitable_Customizer', 'start' ) );
346 404 add_action( 'after_setup_theme', array( $this, 'load_template_files' ) );
@@ -352,19 +410,17 @@
352 410 * as addons) has a chance to run before the event.
353 411 */
354 412 add_action( 'init', array( $this, 'do_charitable_actions' ), 20 );
355 413
356 - If ( $this->load_core_stripe() ) :
414 + if ( $this->load_core_stripe() ) :
357 415
358 416 /**
359 417 * Set up scripts & stylesheets & enqueue them when appropriate.
360 418 */
361 - // add_action( 'wp_enqueue_scripts', array( $this, 'setup_stripe_scripts' ), 11 );
362 419 add_action( 'charitable_form_after_fields', array( $this, 'maybe_setup_stripe_scripts_in_donation_form' ) );
363 420 add_action( 'charitable_campaign_loop_after', array( $this, 'maybe_setup_stripe_scripts_in_campaign_loop' ) );
364 421
365 422 endif;
366 -
367 423 }
368 424
369 425
370 426 /**
@@ -434,25 +490,70 @@
434 490 if ( ! is_admin() ) {
435 491 return false;
436 492 }
437 493
438 - require_once( $this->get_path( 'admin' ) . 'class-charitable-admin.php' );
439 - 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';
440 496
441 - $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';
442 499
443 - $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';
444 509
445 - // Stripe
510 + // connect for upgrade.wpcharitable.com.
511 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
446 512
447 - 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';
448 517
449 - 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();
450 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 +
451 529 return $admin;
452 530 }
453 531
454 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 + /**
455 556 * Checks whether we're on the public-facing side and if so, loads the public-facing functionality.
456 557 *
457 558 * @since 1.0.0
458 559 *
@@ -462,10 +563,18 @@
462 563 if ( is_admin() && ! $this->is_ajax() ) {
463 564 return false;
464 565 }
465 566
466 - require_once( $this->get_path( 'public' ) . 'class-charitable-public.php' );
567 + require_once $this->get_path( 'public' ) . 'class-charitable-public.php';
467 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 +
468 577 $public = Charitable_Public::get_instance();
469 578
470 579 $this->registry->register_object( $public );
471 580
@@ -480,25 +589,51 @@
480 589 * @return boolean
481 590 */
482 591 public function maybe_start_qunit() {
483 592 /* Skip out early if ?qunit isn't included in the request. */
484 - if ( ! array_key_exists( 'qunit', $_GET ) ) {
593 + if ( ! array_key_exists( 'qunit', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
485 594 return false;
486 595 }
487 596
488 - /* The unit tests have to exist. */
489 - if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
490 - return false;
491 - }
597 + /* The unit tests have to exist. */
598 + if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
599 + return false;
600 + }
492 601
493 - 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 + );
494 623
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' );
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' );
498 627
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' );
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' );
501 636 }
502 637
503 638 /**
504 639 * Checks whether the current request is an AJAX request.
@@ -522,10 +657,10 @@
522 657 if ( ! $this->is_ajax() ) {
523 658 return;
524 659 }
525 660
526 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php' );
527 - 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';
528 663 }
529 664
530 665 /**
531 666 * This method is fired after all plugins are loaded and simply fires the charitable_start hook.
@@ -543,9 +678,11 @@
543 678 /**
544 679 * Fires off an action right after Charitable is installed, allowing other
545 680 * plugins/themes to do something at this point.
546 681 *
547 - * @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.
548 685 *
549 686 * @return void
550 687 */
551 688 public function charitable_install() {
@@ -554,9 +691,9 @@
554 691 if ( ! $install ) {
555 692 return;
556 693 }
557 694
558 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
695 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
559 696
560 697 Charitable_Install::finish_installing();
561 698
562 699 do_action( 'charitable_install' );
@@ -561,8 +698,28 @@
561 698
562 699 do_action( 'charitable_install' );
563 700
564 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' );
565 722 }
566 723
567 724 /**
568 725 * Returns whether we are currently in the start phase of the plugin.
@@ -571,9 +728,9 @@
571 728 *
572 729 * @return boolean
573 730 */
574 731 public function is_start() {
575 - return current_filter() == 'charitable_start';
732 + return current_filter() == 'charitable_start'; // phpcs:ignore
576 733 }
577 734
578 735 /**
579 736 * Returns whether the plugin has already started.
@@ -582,9 +739,9 @@
582 739 *
583 740 * @return boolean
584 741 */
585 742 public function started() {
586 - return did_action( 'charitable_start' ) || current_filter() == 'charitable_start';
743 + return did_action( 'charitable_start' ) || current_filter() == 'charitable_start'; // phpcs:ignore
587 744 }
588 745
589 746 /**
590 747 * Returns whether the plugin is being activated.
@@ -593,9 +750,9 @@
593 750 *
594 751 * @return boolean
595 752 */
596 753 public function is_activation() {
597 - return current_filter() == 'activate_charitable/charitable.php';
754 + return current_filter() == 'activate_charitable/charitable.php'; // phpcs:ignore
598 755 }
599 756
600 757 /**
601 758 * Returns whether the plugin is being deactivated.
@@ -604,9 +761,9 @@
604 761 *
605 762 * @return boolean
606 763 */
607 764 public function is_deactivation() {
608 - return current_filter() == 'deactivate_charitable/charitable.php';
765 + return current_filter() == 'deactivate_charitable/charitable.php'; // phpcs:ignore
609 766 }
610 767
611 768 /**
612 769 * Returns plugin paths.
@@ -640,8 +797,12 @@
640 797 case 'templates':
641 798 $path = $base . 'templates/';
642 799 break;
643 800
801 + case 'plugin-directory':
802 + $path = WP_PLUGIN_DIR;
803 + break;
804 +
644 805 case 'directory':
645 806 $path = $base;
646 807 break;
647 808
@@ -671,8 +832,26 @@
671 832 return $version;
672 833 }
673 834
674 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 + /**
675 854 * Get the campaign fields registry
676 855 *
677 856 * If the registry has not been set up yet, this will also
678 857 * perform the task of setting it up initially.
@@ -689,9 +868,9 @@
689 868
690 869 /* Register it immediately to avoid endless recursion. */
691 870 $this->registry->register_object( $campaign_fields );
692 871
693 - $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';
694 873
695 874 foreach ( $fields as $key => $args ) {
696 875 $campaign_fields->register_field( new Charitable_Campaign_Field( $key, $args ) );
697 876 }
@@ -718,9 +897,9 @@
718 897
719 898 /* Register it immediately to avoid endless recursion. */
720 899 $this->registry->register_object( $donation_fields );
721 900
722 - $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';
723 902
724 903 foreach ( $fields as $key => $args ) {
725 904 $donation_fields->register_field( new Charitable_Donation_Field( $key, $args ) );
726 905 }
@@ -752,22 +931,22 @@
752 931 * BEFORE the endpoint it builds on. In other words, move from
753 932 * most specific to least specific.
754 933 */
755 934 $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 );
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() );
770 949
771 950 $this->registry->register_object( $endpoints );
772 951 }
773 952
@@ -781,9 +960,9 @@
781 960 *
782 961 * @param string $class The type of class to be retrieved.
783 962 * @return object
784 963 */
785 - public function get_registered_object( $class ) {
964 + public function get_registered_object( $class ) { // phpcs:ignore
786 965 return $this->registry->get( $class );
787 966 }
788 967
789 968 /**
@@ -834,8 +1013,9 @@
834 1013 /**
835 1014 * Filter the array of available Charitable table classes.
836 1015 *
837 1016 * @since 1.0.0
1017 + * @version 1.8.1
838 1018 *
839 1019 * @param array $tables List of tables as a key=>value array.
840 1020 */
841 1021 return apply_filters(
@@ -840,11 +1020,13 @@
840 1020 */
841 1021 return apply_filters(
842 1022 'charitable_db_tables',
843 1023 array(
844 - 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
845 - 'donors' => 'Charitable_Donors_DB',
846 - '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',
847 1029 )
848 1030 );
849 1031 }
850 1032
@@ -875,14 +1057,14 @@
875 1057 * or just the current site. Multisite only. Default is false.
876 1058 * @return void
877 1059 */
878 1060 public function activate( $network_wide = false ) {
879 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
1061 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
880 1062
881 1063 if ( is_multisite() && $network_wide ) {
882 1064 global $wpdb;
883 1065
884 - 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
885 1067 switch_to_blog( $blog_id );
886 1068 new Charitable_Install( $this->includes_path );
887 1069 restore_current_blog();
888 1070 }
@@ -890,9 +1072,12 @@
890 1072 new Charitable_Install( $this->includes_path );
891 1073 }
892 1074
893 1075 // create or update the install date for future reference.
894 - 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();
895 1080 }
896 1081
897 1082 /**
898 1083 * Runs on plugin deactivation.
@@ -903,9 +1088,9 @@
903 1088 *
904 1089 * @return void
905 1090 */
906 1091 public function deactivate() {
907 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php' );
1092 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php';
908 1093 new Charitable_Uninstall();
909 1094 }
910 1095
911 1096 /**
@@ -915,11 +1100,11 @@
915 1100 *
916 1101 * @return void
917 1102 */
918 1103 public function do_charitable_actions() {
919 - if ( isset( $_REQUEST['charitable_action'] ) ) {
1104 + if ( isset( $_REQUEST['charitable_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
920 1105
921 - $action = $_REQUEST['charitable_action'];
1106 + $action = esc_attr( $_REQUEST['charitable_action'] ); // phpcs:ignore
922 1107
923 1108 /**
924 1109 * Handle Charitable action.
925 1110 *
@@ -995,9 +1180,9 @@
995 1180 *
996 1181 * @param mixed $object Object to be registered.
997 1182 * @return void
998 1183 */
999 - public function register_object( $object ) {
1184 + public function register_object( $object ) { // phpcs:ignore
1000 1185 charitable_get_deprecated()->deprecated_function(
1001 1186 __METHOD__,
1002 1187 '1.5.0',
1003 1188 'charitable()->registry()->register_object()'
@@ -1102,18 +1287,13 @@
1102 1287 if ( false !== ( defined( 'USE_NEW_STRIPE' ) && USE_NEW_STRIPE ) ) {
1103 1288 return true;
1104 1289 }
1105 1290
1106 - // 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 + }
1107 1295
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 1296 return true;
1117 1297 }
1118 1298
1119 1299 /**
@@ -1124,9 +1304,9 @@
1124 1304 * @return boolean
1125 1305 */
1126 1306 public function is_stripe_connect_addon() {
1127 1307
1128 - 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
1129 1309 return true;
1130 1310 }
1131 1311
1132 1312 return false;
@@ -1136,10 +1316,11 @@
1136 1316 * Action links for the plugins page.
1137 1317 *
1138 1318 * @since 1.7.0
1139 1319 *
1140 - * @param array $actions An array of existing actions.
1141 - * @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.
1142 1323 * @return array An array of action links.
1143 1324 */
1144 1325 public function plugin_action_links( $actions, $plugin_file, $action_links = false ) {
1145 1326
@@ -1146,15 +1327,15 @@
1146 1327 if ( ! charitable_is_pro() ) :
1147 1328
1148 1329 // if this isn't pro, add the upgrade link which is visible on the plugins page until the plugin name.
1149 1330
1150 - $action_links = [
1151 - 'proupgrade' => [
1331 + $action_links = array(
1332 + 'proupgrade' => array(
1152 1333 // Translators: This is an action link users can click to purchase a license for All in One SEO Pro.
1153 1334 'label' => __( 'Upgrade to Pro', 'charitable' ),
1154 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',
1155 - ]
1156 - ];
1336 + ),
1337 + );
1157 1338
1158 1339 endif;
1159 1340
1160 1341 if ( isset( $actions['edit'] ) ) {
@@ -1168,23 +1349,24 @@
1168 1349 * Parse the action links.
1169 1350 *
1170 1351 * @since 1.7.0
1171 1352 *
1172 - * @param array $actions
1173 - * @param string $pluginFile
1174 - * @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.
1175 1357 * @return array
1176 1358 */
1177 - 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' ) {
1178 1360 if ( empty( $this->plugin ) ) {
1179 - $this->plugin = CHARTIABLE_PLUGIN_BASENAME;
1361 + $this->plugin = CHARITABLE_PLUGIN_BASENAME;
1180 1362 }
1181 1363
1182 - if ( $this->plugin === $pluginFile && ! empty( $action_links ) ) {
1364 + if ( $this->plugin === $plugin_file && ! empty( $action_links ) ) {
1183 1365 foreach ( $action_links as $key => $value ) {
1184 - $link = [
1185 - $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>'
1186 - ];
1366 + $link = array(
1367 + $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>',
1368 + );
1187 1369
1188 1370 $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
1189 1371 }
1190 1372 }
@@ -1200,11 +1382,11 @@
1200 1382 * @return void
1201 1383 */
1202 1384 public function setup_licensing() {
1203 1385 charitable_get_helper( 'licenses' )->register_licensed_product(
1204 - Charitable::NAME . ' Pro',
1205 - Charitable::AUTHOR,
1206 - Charitable::VERSION,
1386 + self::NAME,
1387 + self::AUTHOR,
1388 + self::VERSION,
1207 1389 __FILE__
1208 1390 );
1209 1391 }
1210 1392
@@ -1211,16 +1393,14 @@
1211 1393 /**
1212 1394 * Deativates and/or warnings about any Charitable verison conflicts.
1213 1395 *
1214 1396 * @since 1.7.0
1215 - *
1216 - * @return null
1217 1397 */
1218 1398 public function check_for_version_conflicts() {
1219 1399
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', '<' ) ) {
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
1223 1403 // manually deactivate plugin.
1224 1404 $found = false;
1225 1405 $current = get_option( 'active_plugins', array() );
1226 1406 $key = array_search( 'charitable-stripe/charitable-stripe.php', $current, true );
@@ -1229,21 +1409,78 @@
1229 1409
1230 1410 unset( $current[ $key ] );
1231 1411 update_option( 'active_plugins', $current );
1232 1412
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' );
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 );
1234 1415
1235 - 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
1236 1417 }
1237 - // charitable_get_notices()->add_error( $message );
1238 - add_option( 'chartiable_version_warning', $message );
1418 + add_option( 'charitable_version_warning', $message );
1239 1419 } else {
1240 - delete_option( 'chartiable_version_warning' );
1420 + delete_option( 'charitable_version_warning' );
1241 1421 }
1242 1422 }
1243 1423 }
1244 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();
1245 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 + }
1246 1483 }
1247 1484
1248 1485 $charitable = new Charitable();
1249 1486