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 | includes/admin/onboarding/class-charitable-setup.php +97 -11 1.8.12.3 → 1.8.13 View file →
@@ -84,17 +84,8 @@
84 84 ),
85 85 );
86 86
87 87 /**
88 - * The private app_server_url property.
89 - *
90 - * @since 1.8.4
91 - *
92 - * @var string
93 - */
94 - private $app_server_url = 'https://app.wpcharitable.com/';
95 -
96 - /**
97 88 * The private $stripe_redirect_url property.
98 89 *
99 90 * @var string
100 91 */
@@ -139,8 +130,13 @@
139 130 // Register the resume-banner dismiss listener unconditionally — it
140 131 // must fire during admin-ajax, which the guards below bail out of.
141 132 add_action( 'charitable_dashboard_notification_dismissed', [ $this, 'maybe_clear_onboarding_state_on_dismiss' ] );
142 133
134 + // Records that the user actually departed for the setup wizard. Like the
135 + // hook above, this MUST be registered before the wp_doing_ajax() bail
136 + // below, or it would never fire on admin-ajax.php.
137 + add_action( 'wp_ajax_charitable_onboarding_started', [ $this, 'ajax_mark_onboarding_started' ] );
138 +
143 139 // If user is in admin ajax or doing cron, return.
144 140 if ( wp_doing_ajax() || wp_doing_cron() ) {
145 141 return;
146 142 }
@@ -280,8 +276,45 @@
280 276 delete_option( 'charitable_started_onboarding' );
281 277 }
282 278
283 279 /**
280 + * Record that the user has actually departed for the setup wizard.
281 + *
282 + * Fired by a beacon sent when the welcome screen's call-to-action is
283 + * clicked. That button links straight out to the centrally hosted wizard,
284 + * so there is no server-side moment at which we would otherwise notice the
285 + * departure.
286 + *
287 + * `charitable_started_onboarding` is what `resume_onboarding()` reads to
288 + * decide someone is mid-setup and pull them back to the welcome screen. It
289 + * used to be set on activation, which meant every new install was treated
290 + * as mid-setup whether or not the user ever went to the wizard. Setting it
291 + * here instead makes the option mean what its name says.
292 + *
293 + * Deliberately best-effort: if the beacon does not arrive (JavaScript
294 + * disabled, browser without sendBeacon, request dropped mid-navigation) the
295 + * user still reaches the wizard normally and simply is not offered the
296 + * resume prompt afterwards. Losing a prompt is an acceptable failure; a
297 + * broken call-to-action on the first screen of a new install is not.
298 + *
299 + * @since 1.8.12.4
300 + *
301 + * @return void
302 + */
303 + public function ajax_mark_onboarding_started() {
304 +
305 + check_ajax_referer( 'charitable_onboarding_started', 'nonce' );
306 +
307 + if ( ! charitable_current_user_can( 'administrator' ) ) {
308 + wp_send_json_error( null, 403 );
309 + }
310 +
311 + update_option( 'charitable_started_onboarding', 1 );
312 +
313 + wp_send_json_success();
314 + }
315 +
316 + /**
284 317 * Onboarding welcome screen redirect.
285 318 *
286 319 * This function checks if a new install or update has just occurred. If so,
287 320 * then we redirect the user to the appropriate page.
@@ -326,9 +359,17 @@
326 359 $upgrade = get_option( 'charitable_version_upgraded_from', [] );
327 360
328 361 if ( ! $upgrade || 1 === count( $upgrade ) ) {
329 362 // Initial install.
330 - update_option( 'charitable_started_onboarding', 1 );
363 + //
364 + // NOTE: `charitable_started_onboarding` is deliberately NOT set here.
365 + // Being redirected to the welcome screen is not the same as having
366 + // started the wizard, and `resume_onboarding()` treats that option as
367 + // "this user is mid-setup" and redirects them back here from other
368 + // admin pages until it is cleared. Setting it on activation therefore
369 + // nagged every new install, including people who never clicked
370 + // through. It is now set only when the user actually leaves for the
371 + // wizard - see `ajax_mark_onboarding_started()`.
331 372 wp_safe_redirect( admin_url( 'admin.php?page=charitable&wpchar_lite=lite&setup=welcome&f=1' ) );
332 373 exit;
333 374 }
334 375 }
@@ -369,8 +410,53 @@
369 410 );
370 411
371 412 wp_enqueue_style( 'charitable-admin-user-onboarding' );
372 413
414 + if ( $this->is_welcome_page() ) :
415 +
416 + /*
417 + * The welcome screen's call-to-action links straight out to the
418 + * centrally hosted wizard, so nothing server-side observes the
419 + * departure. Send a beacon on click so
420 + * `charitable_started_onboarding` is set only for users who
421 + * genuinely leave for the wizard, rather than for everyone who
422 + * merely activated the plugin.
423 + *
424 + * Inline rather than a new asset file: it is a few lines, the
425 + * existing onboarding script is only registered on the setup page
426 + * and pulls in the confetti library, and a new file would need a
427 + * minified twin to match the $min convention here.
428 + */
429 + wp_register_script( 'charitable-onboarding-departure', '', array(), $version, true );
430 + wp_enqueue_script( 'charitable-onboarding-departure' );
431 +
432 + wp_add_inline_script(
433 + 'charitable-onboarding-departure',
434 + '(function(){' .
435 + 'var d=' . wp_json_encode(
436 + array(
437 + 'url' => admin_url( 'admin-ajax.php' ),
438 + 'nonce' => wp_create_nonce( 'charitable_onboarding_started' ),
439 + )
440 + ) . ';' .
441 + 'document.addEventListener("DOMContentLoaded",function(){' .
442 + 'var a=document.querySelector(".charitable-user-onboarding-wrap a.charitable-button-link");' .
443 + 'if(!a){return;}' .
444 + 'a.addEventListener("click",function(){' .
445 + 'try{' .
446 + 'var f=new FormData();' .
447 + 'f.append("action","charitable_onboarding_started");' .
448 + 'f.append("nonce",d.nonce);' .
449 + 'if(navigator.sendBeacon){navigator.sendBeacon(d.url,f);}' .
450 + 'else{fetch(d.url,{method:"POST",body:f,credentials:"same-origin",keepalive:true}).catch(function(){});}' .
451 + '}catch(e){}' .
452 + '});' . // Never preventDefault: navigation to the wizard must proceed regardless.
453 + '});' .
454 + '})();'
455 + );
456 +
457 + endif;
458 +
373 459 if ( $this->is_setup_page() ) :
374 460
375 461 wp_register_script(
376 462 'charitable-admin-user-onboarding',
@@ -1035,9 +1121,9 @@
1035 1121 'return' => rawurlencode(
1036 1122 base64_encode( get_admin_url( null, 'admin.php') ) // phpcs:ignore
1037 1123 ),
1038 1124 ),
1039 - $this->app_server_url . 'optin-charitable_tracking'
1125 + charitable_onboarding_app_url() . '/optin-charitable_tracking'
1040 1126 );
1041 1127
1042 1128 // Send the POST request.
1043 1129 $response = wp_remote_get(