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 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
← All changes | charitable.php +379 -158 1.7.0.14 → 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.14
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.3.1
11 - * Stable tag: 1.7.0.14
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) 2023, 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.14';
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,63 +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' );
228 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-plan.php' );
229 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-product.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';
230 284
231 - require_once( $includes_path . 'stripe/connect/charitable-stripe-connect-functions.php' );
232 - 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';
233 287
234 288 /* Functions & Hooks */
235 - require_once( $includes_path . 'stripe/charitable-stripe-core-functions.php' );
236 - require_once( $includes_path . 'stripe/donations/charitable-stripe-donation-functions.php' );
237 - 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';
238 292
239 293 /* webhooks */
240 - require_once( $includes_path . 'stripe/gateway/class-charitable-stripe-webhook-api.php' );
241 - 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';
242 296
243 297 endif;
244 298
299 + /* Load PayPal Commerce Gateway */
300 + require_once $includes_path . 'gateways/paypal-commerce/charitable-paypal-commerce.php';
245 301 }
246 302
247 303 /**
248 304 * Load the template functions after theme is loaded.
@@ -255,10 +311,10 @@
255 311 */
256 312 public function load_template_files() {
257 313 $includes_path = $this->get_path( 'includes' );
258 314
259 - require_once( $includes_path . 'public/charitable-template-functions.php' );
260 - 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';
261 317 }
262 318
263 319 /**
264 320 * Dynamically loads the class attempting to be instantiated elsewhere in the
@@ -281,18 +337,18 @@
281 337 }
282 338
283 339 /* Autogenerated class map. */
284 340 if ( ! isset( $this->classmap ) ) {
285 - $this->classmap = include( 'includes/autoloader/charitable-class-map.php' );
341 + $this->classmap = include 'includes/autoloader/charitable-class-map.php';
286 342
287 - $this->classmap[ 'Charitable_Gateway_Stripe_AM' ] = 'gateways/class-charitable-gateway-stripe-am.php';
288 - $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';
289 345 }
290 346
291 347 $file_path = isset( $this->classmap[ $class_name ] ) ? $this->get_path( 'includes' ) . $this->classmap[ $class_name ] : false;
292 348
293 349 if ( false !== $file_path && file_exists( $file_path ) && is_file( $file_path ) ) {
294 - require_once( $file_path );
350 + require_once $file_path;
295 351 return true;
296 352 }
297 353 return false;
298 354 }
@@ -319,11 +375,11 @@
319 375 $this->registry->register_object( Charitable_Locations::get_instance() );
320 376 $this->registry->register_object( Charitable_Currency::get_instance() );
321 377 $this->registry->register_object( Charitable_Notifications::get_instance() );
322 378
323 - $this->registry->register_object( new Charitable_Privacy );
324 - $this->registry->register_object( new Charitable_Debugging );
325 - $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() );
326 382 }
327 383
328 384 return $this->registry;
329 385 }
@@ -339,10 +395,10 @@
339 395 add_action( 'wpmu_new_blog', array( $this, 'maybe_activate_charitable_on_new_site' ) );
340 396 add_action( 'plugins_loaded', array( $this, 'charitable_install' ), 100 );
341 397 add_action( 'plugins_loaded', array( $this, 'charitable_start' ), 100 );
342 398 add_action( 'plugins_loaded', array( $this, 'endpoints' ), 100 );
343 - add_action( 'plugins_loaded', array( $this, 'donation_fields' ), 100 );
344 - 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 );
345 401 add_action( 'plugins_loaded', array( $this, 'register_donormeta_table' ) );
346 402 add_action( 'plugins_loaded', 'charitable_load_compat_functions' );
347 403 add_action( 'setup_theme', array( 'Charitable_Customizer', 'start' ) );
348 404 add_action( 'after_setup_theme', array( $this, 'load_template_files' ) );
@@ -354,19 +410,17 @@
354 410 * as addons) has a chance to run before the event.
355 411 */
356 412 add_action( 'init', array( $this, 'do_charitable_actions' ), 20 );
357 413
358 - If ( $this->load_core_stripe() ) :
414 + if ( $this->load_core_stripe() ) :
359 415
360 416 /**
361 417 * Set up scripts & stylesheets & enqueue them when appropriate.
362 418 */
363 - // add_action( 'wp_enqueue_scripts', array( $this, 'setup_stripe_scripts' ), 11 );
364 419 add_action( 'charitable_form_after_fields', array( $this, 'maybe_setup_stripe_scripts_in_donation_form' ) );
365 420 add_action( 'charitable_campaign_loop_after', array( $this, 'maybe_setup_stripe_scripts_in_campaign_loop' ) );
366 421
367 422 endif;
368 -
369 423 }
370 424
371 425
372 426 /**
@@ -436,25 +490,70 @@
436 490 if ( ! is_admin() ) {
437 491 return false;
438 492 }
439 493
440 - require_once( $this->get_path( 'admin' ) . 'class-charitable-admin.php' );
441 - 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';
442 496
443 - $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';
444 499
445 - $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';
446 509
447 - // Stripe
510 + // connect for upgrade.wpcharitable.com.
511 + require_once $this->get_path( 'admin' ) . 'class-charitable-admin-connect.php';
448 512
449 - 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';
450 517
451 - 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();
452 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 +
453 529 return $admin;
454 530 }
455 531
456 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 + /**
457 556 * Checks whether we're on the public-facing side and if so, loads the public-facing functionality.
458 557 *
459 558 * @since 1.0.0
460 559 *
@@ -464,10 +563,18 @@
464 563 if ( is_admin() && ! $this->is_ajax() ) {
465 564 return false;
466 565 }
467 566
468 - require_once( $this->get_path( 'public' ) . 'class-charitable-public.php' );
567 + require_once $this->get_path( 'public' ) . 'class-charitable-public.php';
469 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 +
470 577 $public = Charitable_Public::get_instance();
471 578
472 579 $this->registry->register_object( $public );
473 580
@@ -482,25 +589,51 @@
482 589 * @return boolean
483 590 */
484 591 public function maybe_start_qunit() {
485 592 /* Skip out early if ?qunit isn't included in the request. */
486 - if ( ! array_key_exists( 'qunit', $_GET ) ) {
593 + if ( ! array_key_exists( 'qunit', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
487 594 return false;
488 595 }
489 596
490 - /* The unit tests have to exist. */
491 - if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
492 - return false;
493 - }
597 + /* The unit tests have to exist. */
598 + if ( ! file_exists( $this->get_path( 'directory' ) . 'tests/qunit/tests.js' ) ) {
599 + return false;
600 + }
494 601
495 - 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 + );
496 623
497 - /* Version: '20170615-15:44' */
498 - wp_register_script( 'qunit-tests', $this->get_path( 'directory', false ) . 'tests/qunit/tests.js', array( 'jquery', 'qunit' ), time(), true );
499 - 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' );
500 627
501 - wp_register_style( 'qunit', 'https://code.jquery.com/qunit/qunit-2.3.3.css', array(), '2.3.3', 'all' );
502 - 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' );
503 636 }
504 637
505 638 /**
506 639 * Checks whether the current request is an AJAX request.
@@ -524,10 +657,10 @@
524 657 if ( ! $this->is_ajax() ) {
525 658 return;
526 659 }
527 660
528 - require_once( $this->get_path( 'includes' ) . 'ajax/charitable-ajax-functions.php' );
529 - 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';
530 663 }
531 664
532 665 /**
533 666 * This method is fired after all plugins are loaded and simply fires the charitable_start hook.
@@ -545,9 +678,11 @@
545 678 /**
546 679 * Fires off an action right after Charitable is installed, allowing other
547 680 * plugins/themes to do something at this point.
548 681 *
549 - * @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.
550 685 *
551 686 * @return void
552 687 */
553 688 public function charitable_install() {
@@ -556,9 +691,9 @@
556 691 if ( ! $install ) {
557 692 return;
558 693 }
559 694
560 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
695 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
561 696
562 697 Charitable_Install::finish_installing();
563 698
564 699 do_action( 'charitable_install' );
@@ -566,14 +701,25 @@
566 701 delete_transient( 'charitable_install' );
567 702
568 703 // Store the date when the initial activation was performed.
569 704 $type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'pro' : 'lite';
570 - $activated = get_option( 'charitable_activated', array() );
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 +
571 715 if ( empty( $activated[ $type ] ) ) {
572 716 $activated[ $type ] = time();
573 717 update_option( 'charitable_activated', $activated );
574 718 }
575 719
720 + // Clear notifications.
721 + delete_option( 'charitable_notifications' );
576 722 }
577 723
578 724 /**
579 725 * Returns whether we are currently in the start phase of the plugin.
@@ -582,9 +728,9 @@
582 728 *
583 729 * @return boolean
584 730 */
585 731 public function is_start() {
586 - return current_filter() == 'charitable_start';
732 + return current_filter() == 'charitable_start'; // phpcs:ignore
587 733 }
588 734
589 735 /**
590 736 * Returns whether the plugin has already started.
@@ -593,9 +739,9 @@
593 739 *
594 740 * @return boolean
595 741 */
596 742 public function started() {
597 - return did_action( 'charitable_start' ) || current_filter() == 'charitable_start';
743 + return did_action( 'charitable_start' ) || current_filter() == 'charitable_start'; // phpcs:ignore
598 744 }
599 745
600 746 /**
601 747 * Returns whether the plugin is being activated.
@@ -604,9 +750,9 @@
604 750 *
605 751 * @return boolean
606 752 */
607 753 public function is_activation() {
608 - return current_filter() == 'activate_charitable/charitable.php';
754 + return current_filter() == 'activate_charitable/charitable.php'; // phpcs:ignore
609 755 }
610 756
611 757 /**
612 758 * Returns whether the plugin is being deactivated.
@@ -615,9 +761,9 @@
615 761 *
616 762 * @return boolean
617 763 */
618 764 public function is_deactivation() {
619 - return current_filter() == 'deactivate_charitable/charitable.php';
765 + return current_filter() == 'deactivate_charitable/charitable.php'; // phpcs:ignore
620 766 }
621 767
622 768 /**
623 769 * Returns plugin paths.
@@ -651,8 +797,12 @@
651 797 case 'templates':
652 798 $path = $base . 'templates/';
653 799 break;
654 800
801 + case 'plugin-directory':
802 + $path = WP_PLUGIN_DIR;
803 + break;
804 +
655 805 case 'directory':
656 806 $path = $base;
657 807 break;
658 808
@@ -682,8 +832,26 @@
682 832 return $version;
683 833 }
684 834
685 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 + /**
686 854 * Get the campaign fields registry
687 855 *
688 856 * If the registry has not been set up yet, this will also
689 857 * perform the task of setting it up initially.
@@ -700,9 +868,9 @@
700 868
701 869 /* Register it immediately to avoid endless recursion. */
702 870 $this->registry->register_object( $campaign_fields );
703 871
704 - $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';
705 873
706 874 foreach ( $fields as $key => $args ) {
707 875 $campaign_fields->register_field( new Charitable_Campaign_Field( $key, $args ) );
708 876 }
@@ -729,9 +897,9 @@
729 897
730 898 /* Register it immediately to avoid endless recursion. */
731 899 $this->registry->register_object( $donation_fields );
732 900
733 - $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';
734 902
735 903 foreach ( $fields as $key => $args ) {
736 904 $donation_fields->register_field( new Charitable_Donation_Field( $key, $args ) );
737 905 }
@@ -763,22 +931,22 @@
763 931 * BEFORE the endpoint it builds on. In other words, move from
764 932 * most specific to least specific.
765 933 */
766 934 $endpoints = new Charitable_Endpoints();
767 - $endpoints->register( new Charitable_Campaign_Donation_Endpoint );
768 - $endpoints->register( new Charitable_Campaign_Widget_Endpoint );
769 - $endpoints->register( new Charitable_Campaign_Endpoint );
770 - $endpoints->register( new Charitable_Donation_Cancellation_Endpoint );
771 - $endpoints->register( new Charitable_Donation_Processing_Endpoint );
772 - $endpoints->register( new Charitable_Donation_Receipt_Endpoint );
773 - $endpoints->register( new Charitable_Email_Preview_Endpoint );
774 - $endpoints->register( new Charitable_Email_Verification_Endpoint );
775 - $endpoints->register( new Charitable_Forgot_Password_Endpoint );
776 - $endpoints->register( new Charitable_Reset_Password_Endpoint );
777 - $endpoints->register( new Charitable_Registration_Endpoint );
778 - $endpoints->register( new Charitable_Login_Endpoint );
779 - $endpoints->register( new Charitable_Profile_Endpoint );
780 - $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() );
781 949
782 950 $this->registry->register_object( $endpoints );
783 951 }
784 952
@@ -792,9 +960,9 @@
792 960 *
793 961 * @param string $class The type of class to be retrieved.
794 962 * @return object
795 963 */
796 - public function get_registered_object( $class ) {
964 + public function get_registered_object( $class ) { // phpcs:ignore
797 965 return $this->registry->get( $class );
798 966 }
799 967
800 968 /**
@@ -845,8 +1013,9 @@
845 1013 /**
846 1014 * Filter the array of available Charitable table classes.
847 1015 *
848 1016 * @since 1.0.0
1017 + * @version 1.8.1
849 1018 *
850 1019 * @param array $tables List of tables as a key=>value array.
851 1020 */
852 1021 return apply_filters(
@@ -851,11 +1020,13 @@
851 1020 */
852 1021 return apply_filters(
853 1022 'charitable_db_tables',
854 1023 array(
855 - 'campaign_donations' => 'Charitable_Campaign_Donations_DB',
856 - 'donors' => 'Charitable_Donors_DB',
857 - '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',
858 1029 )
859 1030 );
860 1031 }
861 1032
@@ -886,14 +1057,14 @@
886 1057 * or just the current site. Multisite only. Default is false.
887 1058 * @return void
888 1059 */
889 1060 public function activate( $network_wide = false ) {
890 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php' );
1061 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-install.php';
891 1062
892 1063 if ( is_multisite() && $network_wide ) {
893 1064 global $wpdb;
894 1065
895 - 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
896 1067 switch_to_blog( $blog_id );
897 1068 new Charitable_Install( $this->includes_path );
898 1069 restore_current_blog();
899 1070 }
@@ -901,9 +1072,12 @@
901 1072 new Charitable_Install( $this->includes_path );
902 1073 }
903 1074
904 1075 // create or update the install date for future reference.
905 - 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();
906 1080 }
907 1081
908 1082 /**
909 1083 * Runs on plugin deactivation.
@@ -914,9 +1088,9 @@
914 1088 *
915 1089 * @return void
916 1090 */
917 1091 public function deactivate() {
918 - require_once( $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php' );
1092 + require_once $this->get_path( 'includes' ) . 'plugin/class-charitable-uninstall.php';
919 1093 new Charitable_Uninstall();
920 1094 }
921 1095
922 1096 /**
@@ -926,11 +1100,11 @@
926 1100 *
927 1101 * @return void
928 1102 */
929 1103 public function do_charitable_actions() {
930 - if ( isset( $_REQUEST['charitable_action'] ) ) {
1104 + if ( isset( $_REQUEST['charitable_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
931 1105
932 - $action = $_REQUEST['charitable_action'];
1106 + $action = esc_attr( $_REQUEST['charitable_action'] ); // phpcs:ignore
933 1107
934 1108 /**
935 1109 * Handle Charitable action.
936 1110 *
@@ -1006,9 +1180,9 @@
1006 1180 *
1007 1181 * @param mixed $object Object to be registered.
1008 1182 * @return void
1009 1183 */
1010 - public function register_object( $object ) {
1184 + public function register_object( $object ) { // phpcs:ignore
1011 1185 charitable_get_deprecated()->deprecated_function(
1012 1186 __METHOD__,
1013 1187 '1.5.0',
1014 1188 'charitable()->registry()->register_object()'
@@ -1118,18 +1292,8 @@
1118 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() ) ) {
1119 1293 return false;
1120 1294 }
1121 1295
1122 - // check for stripe addon
1123 -
1124 - // if ( in_array('charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option('active_plugins' ) ) ) ) {
1125 - // return false;
1126 - // }
1127 -
1128 - // if ( class_exists( 'Charitable_Stripe' ) ) {
1129 - // return false;
1130 - // }
1131 -
1132 1296 return true;
1133 1297 }
1134 1298
1135 1299 /**
@@ -1140,9 +1304,9 @@
1140 1304 * @return boolean
1141 1305 */
1142 1306 public function is_stripe_connect_addon() {
1143 1307
1144 - 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
1145 1309 return true;
1146 1310 }
1147 1311
1148 1312 return false;
@@ -1152,10 +1316,11 @@
1152 1316 * Action links for the plugins page.
1153 1317 *
1154 1318 * @since 1.7.0
1155 1319 *
1156 - * @param array $actions An array of existing actions.
1157 - * @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.
1158 1323 * @return array An array of action links.
1159 1324 */
1160 1325 public function plugin_action_links( $actions, $plugin_file, $action_links = false ) {
1161 1326
@@ -1162,15 +1327,15 @@
1162 1327 if ( ! charitable_is_pro() ) :
1163 1328
1164 1329 // if this isn't pro, add the upgrade link which is visible on the plugins page until the plugin name.
1165 1330
1166 - $action_links = [
1167 - 'proupgrade' => [
1331 + $action_links = array(
1332 + 'proupgrade' => array(
1168 1333 // Translators: This is an action link users can click to purchase a license for All in One SEO Pro.
1169 1334 'label' => __( 'Upgrade to Pro', 'charitable' ),
1170 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',
1171 - ]
1172 - ];
1336 + ),
1337 + );
1173 1338
1174 1339 endif;
1175 1340
1176 1341 if ( isset( $actions['edit'] ) ) {
@@ -1184,23 +1349,24 @@
1184 1349 * Parse the action links.
1185 1350 *
1186 1351 * @since 1.7.0
1187 1352 *
1188 - * @param array $actions
1189 - * @param string $pluginFile
1190 - * @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.
1191 1357 * @return array
1192 1358 */
1193 - 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' ) {
1194 1360 if ( empty( $this->plugin ) ) {
1195 - $this->plugin = CHARTIABLE_PLUGIN_BASENAME;
1361 + $this->plugin = CHARITABLE_PLUGIN_BASENAME;
1196 1362 }
1197 1363
1198 - if ( $this->plugin === $pluginFile && ! empty( $action_links ) ) {
1364 + if ( $this->plugin === $plugin_file && ! empty( $action_links ) ) {
1199 1365 foreach ( $action_links as $key => $value ) {
1200 - $link = [
1201 - $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>'
1202 - ];
1366 + $link = array(
1367 + $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>',
1368 + );
1203 1369
1204 1370 $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
1205 1371 }
1206 1372 }
@@ -1216,11 +1382,11 @@
1216 1382 * @return void
1217 1383 */
1218 1384 public function setup_licensing() {
1219 1385 charitable_get_helper( 'licenses' )->register_licensed_product(
1220 - Charitable::NAME, // . ' Pro',
1221 - Charitable::AUTHOR,
1222 - Charitable::VERSION,
1386 + self::NAME,
1387 + self::AUTHOR,
1388 + self::VERSION,
1223 1389 __FILE__
1224 1390 );
1225 1391 }
1226 1392
@@ -1227,16 +1393,14 @@
1227 1393 /**
1228 1394 * Deativates and/or warnings about any Charitable verison conflicts.
1229 1395 *
1230 1396 * @since 1.7.0
1231 - *
1232 - * @return null
1233 1397 */
1234 1398 public function check_for_version_conflicts() {
1235 1399
1236 - // Check for Charitable Stripe < 1.5.0
1237 - if ( in_array( 'charitable-stripe/charitable-stripe.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
1238 - if ( class_exists( 'Charitable_Stripe') && 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
1239 1403 // manually deactivate plugin.
1240 1404 $found = false;
1241 1405 $current = get_option( 'active_plugins', array() );
1242 1406 $key = array_search( 'charitable-stripe/charitable-stripe.php', $current, true );
@@ -1245,21 +1409,78 @@
1245 1409
1246 1410 unset( $current[ $key ] );
1247 1411 update_option( 'active_plugins', $current );
1248 1412
1249 - $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 );
1250 1415
1251 - 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
1252 1417 }
1253 - // charitable_get_notices()->add_error( $message );
1254 - add_option( 'chartiable_version_warning', $message );
1418 + add_option( 'charitable_version_warning', $message );
1255 1419 } else {
1256 - delete_option( 'chartiable_version_warning' );
1420 + delete_option( 'charitable_version_warning' );
1257 1421 }
1258 1422 }
1259 1423 }
1260 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();
1261 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 + }
1262 1483 }
1263 1484
1264 1485 $charitable = new Charitable();
1265 1486