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
charitable / includes / admin / splash / class-charitable-admin-splash.php

class-charitable-admin-splash.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/admin/splash/class-charitable-admin-splash.php

1,026 lines 35.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Admin Splash.
4 *
5 * @package Charitable/Classes/Charitable_Admin_Splash
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.8.6
10 */
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Admin_Splash' ) ) :
18
19 /**
20 * Charitable_Advanced_Settings
21 *
22 * @final
23 * @since 1.8.6
24 */
25 final class Charitable_Admin_Splash {
26
27 /**
28 * The single instance of this class.
29 *
30 * @var Charitable_Admin_Splash|null
31 */
32 private static $instance = null;
33
34 /**
35 * Default plugin version.
36 *
37 * @since 1.8.6
38 *
39 * @var string
40 */
41 private $default_plugin_version = '1.8.6'; // The last version before the "What's New?" feature.
42
43 /**
44 * Previous plugin version.
45 *
46 * @since 1.8.6
47 *
48 * @var string
49 */
50 private $previous_plugin_version;
51
52 /**
53 * Latest splash version.
54 *
55 * @since 1.8.6
56 *
57 * @var string
58 */
59 private $latest_splash_version;
60
61 /**
62 * Splash data.
63 *
64 * @since 1.8.6
65 *
66 * @var array
67 */
68 private $splash_data = array();
69
70 /**
71 * Whether it is a new Charitable installation.
72 *
73 * @since 1.8.6
74 *
75 * @var bool
76 */
77 private $is_new_install;
78
79 /**
80 * Whether the splash link is added.
81 *
82 * @since 1.8.6
83 *
84 * @var bool
85 */
86 private $splash_link_added = false;
87
88 /**
89 * Create object instance.
90 *
91 * @since 1.8.6
92 */
93 private function __construct() {
94 }
95
96 /**
97 * Returns and/or create the single instance of this class.
98 *
99 * @since 1.8.6
100 *
101 * @return Charitable_Admin_Splash
102 */
103 public static function get_instance() {
104 if ( is_null( self::$instance ) ) {
105 self::$instance = new self();
106 }
107
108 return self::$instance;
109 }
110
111 /**
112 * Initialize splash data.
113 *
114 * @since 1.8.6
115 */
116 public function initialize_splash_data() {
117
118 if ( ! $this->is_allow_splash() ) {
119 return;
120 }
121
122 if ( empty( $this->splash_data ) ) {
123 $this->splash_data = $this->get_default_data();
124
125 // Add splash data to a transient.
126 set_transient( 'charitable_splash_data', $this->splash_data, 60 * 60 * 24 );
127
128 $version = $this->get_major_version( charitable()->get_version() );
129
130 $this->update_splash_data_version( $version );
131 }
132 }
133
134 /**
135 * Enqueue assets.
136 *
137 * @since 1.8.6
138 */
139 public function admin_enqueue_scripts() {
140
141 $min = charitable_get_min_suffix();
142
143 if ( ! wp_style_is( 'jquery-confirm', 'enqueued' ) ) {
144 wp_enqueue_style(
145 'jquery-confirm',
146 charitable()->get_path( 'directory', false ) . 'assets/lib/jquery.confirm/jquery-confirm.min.css',
147 null,
148 '3.3.4'
149 );
150 }
151
152 if ( ! wp_script_is( 'jquery-confirm', 'enqueued' ) ) {
153 wp_enqueue_script(
154 'jquery-confirm',
155 charitable()->get_path( 'directory', false ) . 'assets/lib/jquery.confirm/jquery-confirm.min.js',
156 array( 'jquery' ),
157 '3.3.4',
158 false
159 );
160 }
161
162 wp_register_script(
163 'charitable-admin-splash',
164 charitable()->get_path( 'assets', false ) . 'js/admin/charitable-admin-splash' . $min . '.js',
165 array( 'jquery', 'wp-util' ),
166 charitable()->get_version(),
167 true
168 );
169
170 wp_register_style(
171 'charitable-admin-splash',
172 charitable()->get_path( 'assets', false ) . 'css/admin/charitable-admin-splash' . $min . '.css',
173 array(),
174 charitable()->get_version()
175 );
176
177 wp_localize_script(
178 'charitable-admin-splash',
179 'charitable_admin_splash_data',
180 array(
181 'nonce' => wp_create_nonce( 'charitable_admin_splash_nonce' ),
182 'triggerForceOpen' => $this->should_open_splash(),
183 )
184 );
185 }
186
187 /**
188 * Render splash modal.
189 *
190 * @since 1.8.6
191 *
192 * @param array $data Splash modal data.
193 */
194 public function render_modal( array $data = array() ) { // phpcs:ignore
195
196 wp_enqueue_script( 'jquery-confirm' );
197 wp_enqueue_style( 'jquery-confirm' );
198
199 wp_enqueue_script( 'charitable-admin-splash' );
200 wp_enqueue_style( 'charitable-admin-splash' );
201
202 if ( $this->should_open_splash() ) {
203 $this->update_splash_version();
204 }
205
206 // Get splash data from a transient.
207 $this->splash_data = get_transient( 'charitable_splash_data' );
208
209 if ( empty( $this->splash_data ) ) {
210 $this->splash_data = $this->get_default_data();
211 }
212
213 // Always reflect the current plugin major version in the header, even if the transient is stale.
214 $this->splash_data['header']['version'] = $this->get_major_version( charitable()->get_version() );
215
216 $this->splash_data['sections'] = $this->retrieve_sections_for_user( $this->splash_data['sections'] ?? array() );
217
218 $template_location = '/admin/templates/splash/splash-modal';
219 $template_location = apply_filters( 'charitable_admin_splash_modal_template_location', $template_location );
220 echo charitable_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
221 $template_location,
222 array(
223 'data' => $this->splash_data,
224 ),
225 true
226 );
227 }
228
229 /**
230 * Retrieve sections for user.
231 *
232 * @since 1.8.6
233 *
234 * @param array $sections Sections.
235 * @return array Sections.
236 */
237 public function retrieve_sections_for_user( array $sections = array() ): array {
238
239 $sections = array(
240 /*
241 * 1.8.13 — AI assistants, the headline feature.
242 *
243 * This section is FIRST on purpose: the dark treatment comes from
244 * `.charitable-splash-section:first-child` in the compiled
245 * charitable-admin-splash.css (background #2c3338, white text), so the
246 * marquee feature gets that look by position, not by a class of its own.
247 * Reorder these and the dark block follows whatever ends up first.
248 *
249 * Ported from the Pro modal, where the same section leads Pro 1.8.19.
250 * The copy is Pro's with one change: Pro reads "New in 1.8.19: switch on
251 * write access", because Pro 1.8.18 shipped the read-only assistant first.
252 * Lite never had that intermediate release — abilities and the write gate
253 * both land here in 1.8.13 — so the clause is stated plainly instead of
254 * pinned to a version Lite has no concept of.
255 *
256 * Video and poster are the same CDN objects Pro points at; both are live.
257 * The 1-8-19 path is Pro's release folder, not a Lite version — the bucket
258 * is shared, and the PayPal section below already does the same thing with
259 * splash/1-8-15 under a Lite 1.8.11 badge.
260 */
261 array(
262 'new' => true,
263 'version' => '1.8.13',
264 'layout' => 'fifty-fifty',
265 'class' => 'no-order',
266 'title' => __( 'Build Campaigns Just By Asking Your AI Assistant', 'charitable' ),
267 'content' => __( 'Connect an assistant like Claude or ChatGPT using the free WPVibe plugin, or any MCP client, then just say what you need. Switch on write access and it makes the change itself. Launch a fundraiser, add a question to a donation form, restyle a campaign, or log a check that came in the post.', 'charitable' ),
268 'video' => array(
269 'url' => 'https://wpcharitable-space.nyc3.digitaloceanspaces.com/splash/1-8-19/ai-assistants.mp4',
270 'poster' => 'https://wpcharitable-space.nyc3.digitaloceanspaces.com/splash/1-8-19/ai-assistants-poster.jpg',
271 ),
272 'buttons' => array(
273 'main' => array(
274 'text' => __( 'Get Started', 'charitable' ),
275 'url' => charitable_utm_link( 'https://www.wpcharitable.com/ai-assistants-getting-started/', 'splash-modal', 'AI Assistants Main' ),
276 ),
277 'secondary' => array(
278 'text' => __( 'Learn More', 'charitable' ),
279 'url' => charitable_utm_link( 'https://www.wpcharitable.com/ai-assistants-learn-more/', 'splash-modal', 'AI Assistants Secondary' ),
280 ),
281 ),
282 ),
283 /*
284 * Charitable Events, slot 2. Ported from the Pro modal, where it carries
285 * the 'new-addon' badge — Events ships on its own 1.0.0 cycle and is not a
286 * Pro 1.8.19 feature. Here it is 'new-for-pro' instead, because for a Lite
287 * reader the relevant fact is that it needs Pro, not that it is new.
288 *
289 * Buttons follow the Lite house style for Pro sections (main + upgrade)
290 * rather than Pro's main + secondary. That is also what keeps the second
291 * button alive: Pro's Learn More points at
292 * /charitable-events-learn-more/, which is not published yet and 404s.
293 */
294 array(
295 'new-for-pro' => true,
296 'layout' => 'fifty-fifty',
297 'class' => 'no-order',
298 'title' => __( 'Charitable Events', 'charitable' ),
299 'content' => __( 'Sell tickets to galas, dinners and virtual events without leaving Charitable. Every ticket is a donation, so the money lands in the event\'s own campaign. Set a fair market value per ticket and the deductible portion appears on the receipt. Each attendee gets a QR code, and you check them in at the door from your phone.', 'charitable' ),
300 'img' => array(
301 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-19-charitable-events.png',
302 'shadow' => 'none',
303 ),
304 'buttons' => array(
305 'main' => array(
306 'text' => __( 'Get Started', 'charitable' ),
307 'url' => charitable_utm_link( 'https://www.wpcharitable.com/charitable-events-getting-started/', 'splash-modal', 'Charitable Events Main' ),
308 ),
309 'upgrade' => array(
310 'text' => __( 'Upgrade to Pro', 'charitable' ),
311 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Charitable Events Upgrade' ),
312 ),
313 ),
314 ),
315 // 1.8.12 — Beacon campaign templates.
316 array(
317 'new' => true,
318 'version' => '1.8.12',
319 'layout' => 'fifty-fifty',
320 'class' => 'no-order',
321 'title' => __( 'New Beacon Campaign Templates', 'charitable' ),
322 'content' => __( 'Two new campaign templates — Beacon (1 Column) and Beacon (2 Column) — built on the new Hero layout. Pick either from the template chooser for a bold, modern campaign page.', 'charitable' ),
323 'img' => array(
324 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-12-beacon.jpg',
325 'shadow' => 'none',
326 ),
327 'buttons' => array(
328 'main' => array(
329 'text' => __( 'Get Started', 'charitable' ),
330 'url' => charitable_utm_link( 'https://www.wpcharitable.com/beacon-templates-getting-started/', 'splash-modal', 'Beacon Templates Main' ),
331 ),
332 'secondary' => array(
333 'text' => __( 'Learn More', 'charitable' ),
334 'url' => charitable_utm_link( 'https://www.wpcharitable.com/beacon-templates-learn-more/', 'splash-modal', 'Beacon Templates Secondary' ),
335 ),
336 ),
337 ),
338 // PayPal Commerce — headline feature, uses same self-hosted video as Pro.
339 array(
340 'new' => true,
341 'version' => '1.8.11',
342 'layout' => 'fifty-fifty',
343 'class' => 'no-order',
344 'title' => __( 'PayPal Commerce', 'charitable' ),
345 'content' => __( 'Accept donations through PayPal, Venmo, Pay Later, cards, Apple Pay, and Google Pay from a single integration. One-click Connect with PayPal setup, with support for one-time and recurring donations, saved payment methods, and refunds handled in the donation admin.', 'charitable' ),
346 'video' => array(
347 'url' => 'https://wpcharitable-space.nyc3.digitaloceanspaces.com/splash/1-8-15/paypal-commerce.mp4',
348 ),
349 'buttons' => array(
350 'main' => array(
351 'text' => __( 'Get Started', 'charitable' ),
352 'url' => charitable_utm_link( 'https://www.wpcharitable.com/paypal-commerce-getting-started/', 'splash-modal', 'PayPal Commerce Main' ),
353 ),
354 'secondary' => array(
355 'text' => __( 'Learn More', 'charitable' ),
356 'url' => charitable_utm_link( 'https://www.wpcharitable.com/paypal-commerce-learn-more/', 'splash-modal', 'PayPal Commerce Secondary' ),
357 ),
358 ),
359 ),
360 // 1.8.10 — Lite headline feature: Migration & Import Tools.
361 array(
362 'new' => true,
363 'version' => '1.8.10',
364 'layout' => 'fifty-fifty',
365 'class' => 'no-order',
366 'title' => __( 'Migration & Import Tools', 'charitable' ),
367 'content' => __( 'Move from GiveWP or GiveButter to Charitable in minutes. Expanded import tools include CSV donations import and a new GiveWP Migration Tool (Beta) under a redesigned, tabbed interface.', 'charitable' ),
368 'img' => array(
369 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-10-import-tools.svg',
370 'shadow' => 'none',
371 ),
372 'buttons' => array(
373 'main' => array(
374 'text' => __( 'Get Started', 'charitable' ),
375 'url' => charitable_utm_link( 'https://www.wpcharitable.com/how-to-switch-from-givewp-to-charitable/', 'splash-modal', 'Import Tools Main' ),
376 ),
377 ),
378 ),
379 // 1.8.17 — DonationExpress (Pro-only).
380 array(
381 'new-for-pro' => true,
382 'layout' => 'fifty-fifty',
383 'class' => 'no-order',
384 'title' => __( 'DonationExpress ⚡', 'charitable' ),
385 'content' => __( 'DonationExpress opens your donation forms in a full-screen, theme-independent sheet on phones. It loads fast, looks consistent on every device, and avoids theme conflicts.', 'charitable' ),
386 'video' => array(
387 'url' => 'https://wpcharitable-space.nyc3.digitaloceanspaces.com/splash/1-8-17/donationexpress.mp4',
388 ),
389 'buttons' => array(
390 'main' => array(
391 'text' => __( 'Get Started', 'charitable' ),
392 'url' => charitable_utm_link( 'https://www.wpcharitable.com/donationexpress-getting-started/', 'splash-modal', 'DonationExpress Main' ),
393 ),
394 'upgrade' => array(
395 'text' => __( 'Upgrade to Pro', 'charitable' ),
396 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'DonationExpress Upgrade' ),
397 ),
398 ),
399 ),
400 // 1.8.17 — Style Your Campaigns Visually (Pro-only).
401 array(
402 'new-for-pro' => true,
403 'layout' => 'fifty-fifty',
404 'class' => 'no-order',
405 'title' => __( 'Style Your Campaigns Visually', 'charitable' ),
406 'content' => __( 'Campaign styling just got a major upgrade. Beyond the existing options, the new Styles tab brings ready-made design presets, a full global color palette (buttons, tabs, progress bar, text, and background), typography controls, and animations — all with a live preview and no code.', 'charitable' ),
407 'img' => array(
408 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-17-campaign-styles.png',
409 'shadow' => 'none',
410 ),
411 'buttons' => array(
412 'main' => array(
413 'text' => __( 'Get Started', 'charitable' ),
414 'url' => charitable_utm_link( 'https://www.wpcharitable.com/campaign-builder-styles-getting-started/', 'splash-modal', 'Campaign Builder Styles Main' ),
415 ),
416 'upgrade' => array(
417 'text' => __( 'Upgrade to Pro', 'charitable' ),
418 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Campaign Builder Styles Upgrade' ),
419 ),
420 ),
421 ),
422 // 1.8.14 — Campaign Countdown (Pro-only).
423 array(
424 'new-for-pro' => true,
425 'layout' => 'fifty-fifty',
426 'class' => 'no-order',
427 'title' => __( 'Campaign Countdown', 'charitable' ),
428 'content' => __( 'Add a countdown timer to any campaign with the new Campaign Builder field or Gutenberg block. Fully customizable, with a built-in fireworks effect when the campaign ends.', 'charitable' ),
429 'img' => array(
430 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-14-campaign-countdown.png',
431 'shadow' => 'none',
432 ),
433 'buttons' => array(
434 'main' => array(
435 'text' => __( 'Get Started', 'charitable' ),
436 'url' => charitable_utm_link( 'https://www.wpcharitable.com/campaign-countdown-getting-started/', 'splash-modal', 'Campaign Countdown Main' ),
437 ),
438 'upgrade' => array(
439 'text' => __( 'Upgrade to Pro', 'charitable' ),
440 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Campaign Countdown Upgrade' ),
441 ),
442 ),
443 ),
444 // 1.8.9 — Lite security feature.
445 array(
446 'new' => true,
447 'version' => '1.8.9',
448 'layout' => 'fifty-fifty',
449 'class' => 'no-order',
450 'title' => __( 'Security Enhancements', 'charitable' ),
451 'content' => __( 'Charitable Lite now supports Google reCAPTCHA, hCaptcha, and Cloudflare Turnstile for improved security.', 'charitable' ),
452 'img' => array(
453 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-9-security.svg',
454 'shadow' => 'none',
455 ),
456 'buttons' => array(
457 'main' => array(
458 'text' => __( 'Get Started', 'charitable' ),
459 'url' => charitable_utm_link( 'https://www.wpcharitable.com/introducing-improved-security-and-clean-donation-tool/', 'splash-modal', 'Security Main' ),
460 ),
461 ),
462 ),
463 // 1.8.14 — Donorbox CSV Importer (Pro-only).
464 array(
465 'new-for-pro' => true,
466 'layout' => 'fifty-fifty',
467 'class' => 'no-order',
468 'title' => __( 'Donorbox CSV Importer', 'charitable' ),
469 'content' => __( 'Move from Donorbox to Charitable in a single CSV upload. Import historical donations, donors, campaigns, and recurring plans with preview, dry-run, and rollback.', 'charitable' ),
470 'img' => array(
471 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-14-donorbox-importer.png',
472 'shadow' => 'none',
473 ),
474 'buttons' => array(
475 'main' => array(
476 'text' => __( 'Get Started', 'charitable' ),
477 'url' => charitable_utm_link( 'https://www.wpcharitable.com/donorbox-csv-importer-getting-started/', 'splash-modal', 'Donorbox CSV Importer Main' ),
478 ),
479 'upgrade' => array(
480 'text' => __( 'Upgrade to Pro', 'charitable' ),
481 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Donorbox CSV Importer Upgrade' ),
482 ),
483 ),
484 ),
485 // 1.8.13 — Mini Donation Widget (Pro-only, Vimeo embed for Lite).
486 array(
487 'new-for-pro' => true,
488 'layout' => 'fifty-fifty',
489 'class' => 'no-order',
490 'title' => __( 'Mini Donation Widget', 'charitable' ),
491 'content' => __( 'Embed a compact donation form anywhere on your site, no page redirect required. Add it to sidebars, landing pages, or any widget area using a simple block or shortcode.', 'charitable' ),
492 'video' => array(
493 'vimeo_id' => '1186687749',
494 ),
495 'buttons' => array(
496 'main' => array(
497 'text' => __( 'Get Started', 'charitable' ),
498 'url' => charitable_utm_link( 'https://www.wpcharitable.com/documentation/mini-donation-widget/', 'splash-modal', 'Mini Donation Widget Main' ),
499 ),
500 'upgrade' => array(
501 'text' => __( 'Upgrade to Pro', 'charitable' ),
502 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Mini Donation Widget Upgrade' ),
503 ),
504 ),
505 ),
506 // Pro upsells.
507 array(
508 'new-for-pro' => true,
509 'layout' => 'fifty-fifty',
510 'class' => 'no-order',
511 'title' => __( 'Campaign Showcase', 'charitable' ),
512 'content' => __( 'Display all your campaigns beautifully with full layout control including grid, list, or masonry. No coding required.', 'charitable' ),
513 'img' => array(
514 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-13-campaign-showcase.png',
515 'shadow' => 'none',
516 ),
517 'buttons' => array(
518 'main' => array(
519 'text' => __( 'Get Started', 'charitable' ),
520 'url' => charitable_utm_link( 'https://www.wpcharitable.com/campaign-showcase-getting-started/', 'splash-modal', 'Campaign Showcase Main' ),
521 ),
522 'upgrade' => array(
523 'text' => __( 'Upgrade to Pro', 'charitable' ),
524 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Campaign Showcase Upgrade' ),
525 ),
526 ),
527 ),
528 array(
529 'new-for-pro' => true,
530 'layout' => 'fifty-fifty',
531 'class' => 'no-order',
532 'title' => __( 'Donations Feed', 'charitable' ),
533 'content' => __( 'Display recent donations in beautiful list or card views with sorting, filtering, pagination, and live polling that automatically refreshes when new donations arrive.', 'charitable' ),
534 'img' => array(
535 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-13-donate-feed.png',
536 'shadow' => 'none',
537 ),
538 'buttons' => array(
539 'main' => array(
540 'text' => __( 'Get Started', 'charitable' ),
541 'url' => charitable_utm_link( 'https://www.wpcharitable.com/donations-feed-getting-started/', 'splash-modal', 'Donations Feed Main' ),
542 ),
543 'upgrade' => array(
544 'text' => __( 'Upgrade to Pro', 'charitable' ),
545 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Donations Feed Upgrade' ),
546 ),
547 ),
548 ),
549 array(
550 'new-for-pro' => true,
551 'layout' => 'fifty-fifty',
552 'class' => 'no-order',
553 'title' => __( 'Campaign Modal Button', 'charitable' ),
554 'content' => __( 'Add a donate button anywhere on your site that opens a donation form in a modal popup, no page redirect required. Available as a block or shortcode.', 'charitable' ),
555 'img' => array(
556 'url' => charitable()->get_path( 'assets', false ) . 'images/splash/1-8-13-modal-button.png',
557 'shadow' => 'none',
558 ),
559 'buttons' => array(
560 'main' => array(
561 'text' => __( 'Get Started', 'charitable' ),
562 'url' => charitable_utm_link( 'https://www.wpcharitable.com/campaign-modal-button-getting-started/', 'splash-modal', 'Campaign Modal Button Main' ),
563 ),
564 'upgrade' => array(
565 'text' => __( 'Upgrade to Pro', 'charitable' ),
566 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'Campaign Modal Button Upgrade' ),
567 ),
568 ),
569 ),
570 // Two-column "More Recent Features" list.
571 array(
572 'layout' => 'more-features',
573 'class' => 'no-order',
574 'title' => __( 'More Recent Features:', 'charitable' ),
575 'items' => array(
576 __( 'Prefill Donation Forms', 'charitable' ),
577 __( 'Campaign Featured Image', 'charitable' ),
578 __( 'Campaign Selector', 'charitable' ),
579 __( 'Envira Gallery Integration', 'charitable' ),
580 __( 'Visual Form Builder', 'charitable' ),
581 __( 'Donor Leaderboards', 'charitable' ),
582 __( 'Magic Donor Dashboard Link', 'charitable' ),
583 __( 'DIVI Integration', 'charitable' ),
584 __( 'DonorTrust', 'charitable' ),
585 __( 'Google Analytics', 'charitable' ),
586 ),
587 ),
588 );
589
590 return $sections;
591 }
592
593 /**
594 * Check if splash data is empty.
595 *
596 * @since 1.8.6
597 *
598 * @return bool True if empty, false otherwise.
599 */
600 public function is_splash_empty(): bool {
601
602 if ( empty( $this->splash_data ) ) {
603 return true;
604 }
605
606 return empty( $this->retrieve_sections_for_user( $this->splash_data['sections'] ?? array() ) );
607 }
608
609 /**
610 * Output splash modal.
611 *
612 * @since 1.8.6
613 */
614 public function admin_footer() {
615 if ( ! $this->is_allow_splash() ) {
616 return;
617 }
618
619 $this->render_modal();
620 }
621
622 /**
623 * Get splash data version.
624 *
625 * @since 1.8.6
626 *
627 * @return string Splash data version.
628 */
629 private function get_splash_data_version(): string {
630
631 return get_option( 'charitable_splash_data_version', charitable()->get_version() );
632 }
633
634 /**
635 * Update splash data version.
636 *
637 * @since 1.8.6
638 *
639 * @param string $version Splash data version.
640 */
641 private function update_splash_data_version( string $version ) {
642
643 update_option( 'charitable_splash_data_version', $version );
644 }
645
646 /**
647 * Get the latest splash version.
648 *
649 * @since 1.8.6
650 *
651 * @return string Splash version.
652 */
653 private function get_latest_splash_version(): string {
654
655 if ( $this->latest_splash_version ) {
656 return $this->latest_splash_version;
657 }
658
659 $this->latest_splash_version = get_option( 'charitable_splash_version', '1.8.6' );
660
661 // Create option if it doesn't exist.
662 if ( empty( $this->latest_splash_version ) ) {
663 $this->latest_splash_version = $this->default_plugin_version;
664
665 update_option( 'charitable_splash_version', $this->latest_splash_version );
666 }
667
668 return $this->latest_splash_version;
669 }
670
671 /**
672 * Update option with the latest splash version.
673 *
674 * @since 1.8.6
675 */
676 private function update_splash_version() {
677
678 update_option( 'charitable_splash_version', $this->get_major_version( charitable()->get_version() ) );
679 }
680
681 /**
682 * Get a major version.
683 *
684 * @since 1.8.6
685 *
686 * @param string $version Version.
687 *
688 * @return string Major version.
689 */
690 private function get_major_version( $version ): string {
691
692 // Allow only digits and dots.
693 $clean_version = preg_replace( '/[^0-9.]/', '.', $version );
694
695 // Get version parts.
696 $version_parts = explode( '.', $clean_version );
697
698 // If a version has more than 3 parts - use only first 3. Get block data only for major versions.
699 if ( count( $version_parts ) > 3 ) {
700 $version = implode( '.', array_slice( $version_parts, 0, 3 ) );
701 }
702
703 return $version;
704 }
705
706 /**
707 * Get user license type.
708 *
709 * @since 1.8.6
710 *
711 * @return string
712 */
713 private function get_user_license(): string {
714
715 /**
716 * License type used for splash screen.
717 *
718 * @since 1.8.6
719 *
720 * @param string $license License type.
721 */
722 return (string) apply_filters( 'charitable_admin_splash_splashtrait_get_user_license', 'lite' );
723 }
724
725 /**
726 * Get default splash modal data.
727 *
728 * @since 1.8.6
729 *
730 * @return array Splash modal data.
731 */
732 private function get_default_data(): array {
733
734 $default_data = array(
735 'license' => $this->get_user_license(),
736 'buttons' => array(
737 'get_started' => __( 'Get Started', 'charitable' ),
738 'learn_more' => __( 'Learn More', 'charitable' ),
739 ),
740 'header' => array(
741 'image' => charitable()->get_path( 'assets', false ) . 'images/charitable-logo.svg',
742 'title' => __( 'What\'s New in Charitable', 'charitable' ),
743 'description' => __( 'Since you\'ve been gone, we\'ve added some great new features to help grow your campaigns and generate more donations. Here are some highlights...', 'charitable' ),
744 'version' => $this->get_major_version( charitable()->get_version() ),
745 ),
746
747 );
748
749 // If the chartiable_pro is active, that means they are licensed but not using Charitable Pro plugin.
750 if ( ! charitable_is_pro() ) :
751 $default_data['footer'] = array(
752 'title' => __( 'Add Your License To Activate Charitable Pro Plugin Now And Start Getting More Donations!', 'charitable' ),
753 'description' => __( 'Charitable Pro is a powerful upgrade that allows you to manage donors along with built-in features like videos, donor comments, PDF receipts, a dashboard for donors, and more.', 'charitable' ),
754 'upgrade' => array(
755 'text' => __( 'Learn More', 'charitable' ),
756 'url' => charitable_utm_link( 'https://www.wpcharitable.com/lite-upgrade/', 'splash-modal', 'learn-more' ),
757 ),
758 );
759 else :
760 $default_data['footer'] = array(
761 'title' => __( 'Upgrade To The Charitable Pro Plugin At No Cost!', 'charitable' ),
762 'description' => __( 'Registered users with active license can upgrade to Charitable Pro plugin at NO COST. It\'s included in all plans (basic, plus, pro, elite).', 'charitable' ),
763 'upgrade' => array(
764 'text' => __( 'Learn More', 'charitable' ),
765 'url' => charitable_utm_link( 'https://www.wpcharitable.com/pricing/upgrade-lite-to-pro/', 'splash-modal', 'learn-more' ),
766 ),
767 );
768 endif;
769
770 return $default_data;
771 }
772
773 /**
774 * Determine if the current update is a minor update.
775 *
776 * Checks the charitable_upgrade_log option for the latest upgrade entry
777 * and compares its version with the current version to determine if this is
778 * a minor update (same major version) or major update (different major version).
779 *
780 * @since 1.8.6
781 *
782 * @return bool True if it's a minor update, false otherwise.
783 */
784 private function is_minor_update(): bool {
785 $upgrade_log = get_option( 'charitable_upgrade_log', array() );
786
787 // If no upgrade log exists, this is not a minor update.
788 if ( empty( $upgrade_log ) ) {
789 return false;
790 }
791
792 // Get the latest upgrade entry.
793 $latest_entry = end( $upgrade_log );
794
795 // If no version in the latest entry, this is not a minor update.
796 if ( ! isset( $latest_entry['version'] ) ) {
797 return false;
798 }
799
800 $latest_version = $latest_entry['version'];
801 $current_version = charitable()->get_version();
802
803 // Get major versions (e.g., 1.8.5 from 1.8.5.1).
804 $latest_major = $this->get_major_version( $latest_version );
805 $current_major = $this->get_major_version( $current_version );
806
807 // If major versions match, this is a minor update.
808 return $latest_major === $current_major;
809 }
810
811 /**
812 * Check if splash modal is allowed.
813 * Only allow on Charitable dashboard or settings screens; never on onboarding/setup welcome.
814 *
815 * @since 1.8.6
816 *
817 * @return bool True if allowed, false otherwise.
818 */
819 public function is_allow_splash(): bool {
820
821 // Never show on onboarding/setup welcome flow (splash would sit on top of onboarding UI).
822 if ( $this->is_onboarding_setup_page() ) {
823 return false;
824 }
825
826 // Only show on Charitable pages OR dashboard.
827 return charitable_is_admin_screen() || $this->is_dashboard();
828 }
829
830 /**
831 * Check if current screen is the onboarding/setup welcome flow.
832 * Splash must not show here so it doesn't overlay the onboarding UI.
833 *
834 * @since 1.8.9.6
835 *
836 * @return bool True if on onboarding setup page, false otherwise.
837 */
838 private function is_onboarding_setup_page(): bool {
839
840 if ( ! is_admin() || empty( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
841 return false;
842 }
843
844 $page = sanitize_text_field( $_GET['page'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
845
846 // Block splash on the setup wizard, checklist, and any charitable-setup* pages.
847 if ( strpos( $page, 'charitable-setup' ) !== false ) {
848 return true;
849 }
850
851 // Block splash on the welcome/onboarding flow (page=charitable&wpchar_lite=lite&setup=...).
852 if ( 'charitable' !== $page ) {
853 return false;
854 }
855
856 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
857 if ( empty( $_GET['wpchar_lite'] ) || 'lite' !== $_GET['wpchar_lite'] ) {
858 return false;
859 }
860
861 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
862 if ( empty( $_GET['setup'] ) ) {
863 return false;
864 }
865
866 $setup = sanitize_key( $_GET['setup'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
867
868 return in_array( $setup, array( 'welcome', 'cancelled', 'return' ), true );
869 }
870
871 /**
872 * Check if splash modal should be opened.
873 *
874 * @since 1.8.6
875 *
876 * @return bool True if splash should open, false otherwise.
877 */
878 private function should_open_splash(): bool {
879
880 // Skip if announcements are hidden, or it is the dashboard page.
881 if ( $this->is_dashboard() || $this->hide_splash_modal() ) {
882 return false;
883 }
884
885 // If we are forcing the preview, then we should open the splash.
886 if ( $this->is_force_open() ) {
887 return true;
888 }
889
890 if ( ! $this->is_allow_splash() ) {
891 return false;
892 }
893
894 // Allow if a splash version different from the current plugin major version, and it's not a new installation.
895 if ( charitable_is_debug( 'splash' ) ) {
896 // phpcs:disable
897 error_log( 'Charitable Admin Splash: Latest splash version: ' . $this->get_latest_splash_version() );
898 error_log( 'Charitable Admin Splash: Current major version: ' . $this->get_major_version( charitable()->get_version() ) );
899 error_log( 'Charitable Admin Splash: Is new install: ' . ( $this->is_new_install() ? 'true' : 'false' ) );
900 error_log( 'Charitable Admin Splash: Is force open: ' . ( $this->is_force_open() ? 'true' : 'false' ) );
901 // phpcs:enable
902 }
903
904 $should_open_splash = $this->get_latest_splash_version() !== $this->get_major_version( charitable()->get_version() ) &&
905 ( ! $this->is_new_install() || $this->is_force_open() );
906
907 if ( ! $should_open_splash ) {
908 return false;
909 }
910
911 // Skip if user on the builder page and the Challenge can be started.
912 if ( $this->is_builder_page() ) {
913 return false;
914 }
915
916 return true;
917 }
918
919 /**
920 * Check if the current page is the builder page.
921 *
922 * @since 1.8.6
923 *
924 * @return bool True if it is the builder page, false otherwise.
925 */
926 private function is_builder_page(): bool {
927 return ! empty( $_GET['page'] ) && 'charitable-campaign-builder' === $_GET['page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
928 }
929
930 /**
931 * Check if the current page is the dashboard.
932 *
933 * @since 1.8.6
934 *
935 * @return bool True if it is the dashboard, false otherwise.
936 */
937 private function is_dashboard(): bool {
938
939 global $pagenow;
940
941 if ( ! empty( $_GET['page'] ) && 'charitable-dashboard' === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
942 return true;
943 }
944
945 return false;
946 }
947
948 /**
949 * Check if splash modal should be forced open.
950 *
951 * @since 1.8.6
952 *
953 * @return bool True if it should be forced open, false otherwise.
954 */
955 private function is_force_open(): bool {
956
957 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
958 return sanitize_key( $_GET['charitable_action'] ?? '' ) === 'preview-splash-screen';
959 }
960
961 /**
962 * Check if the plugin is newly installed.
963 *
964 * Checks the charitable_upgrade_log option for the initial installation entry
965 * and compares its version with the current version to determine if this is
966 * a new installation.
967 *
968 * @since 1.8.6
969 *
970 * @return bool True if new install, false otherwise.
971 */
972 private function is_new_install(): bool {
973 $upgrade_log = get_option( 'charitable_upgrade_log', array() );
974
975 // If no upgrade log exists, this is a new install.
976 if ( empty( $upgrade_log ) ) {
977 return true;
978 }
979
980 // Find the initial installation entry.
981 $install_entry = null;
982 foreach ( $upgrade_log as $entry ) {
983 if ( isset( $entry['message'] ) && 'Charitable was installed.' === $entry['message'] ) {
984 $install_entry = $entry;
985 break;
986 }
987 }
988
989 // If no installation entry found, this is a new install.
990 if ( ! $install_entry || ! isset( $install_entry['version'] ) ) {
991 return true;
992 }
993
994 $installed_version = $install_entry['version'];
995 $current_version = charitable()->get_version();
996
997 // Get major versions (e.g., 1.8.5 from 1.8.5.1).
998 $installed_major = $this->get_major_version( $installed_version );
999 $current_major = $this->get_major_version( $current_version );
1000
1001 // If major versions match and current version is not greater, this is a new install.
1002 return $installed_major === $current_major && version_compare( $current_version, $installed_version, '<=' );
1003 }
1004
1005 /**
1006 * Check if splash modal should be hidden.
1007 *
1008 * @since 1.8.6
1009 *
1010 * @return bool True if hidden, false otherwise.
1011 */
1012 private function hide_splash_modal(): bool {
1013
1014 /**
1015 * Force to hide splash modal.
1016 *
1017 * @since 1.8.6
1018 *
1019 * @param bool $hide_splash_modal True to hide, false otherwise.
1020 */
1021 return (bool) apply_filters( 'charitable_admin_splash_screen_hide_splash_modal', charitable_get_option( 'hide_announcements' ) );
1022 }
1023 }
1024
1025 endif;
1026