← All changes
|
includes/core/rest-api/Controllers/DashboardController.php
+106
-3
3.12.10
→
3.13.3
View file →
| @@ -16,8 +16,19 @@ | ||
| 16 | 16 | class DashboardController extends Wpfnl_REST_Controller |
| 17 | 17 | { |
| 18 | 18 | |
| 19 | 19 | /** |
| 20 | + * Transient holding the last known answer to "is the onboarding checklist | |
| 21 | + * on show?", read by the admin page so it can decide whether to hold space | |
| 22 | + * for it while the real status loads. | |
| 23 | + * | |
| 24 | + * @var string | |
| 25 | + * @since 3.13.3 | |
| 26 | + */ | |
| 27 | + const ONBOARDING_VISIBILITY_KEY = 'wpfnl_onboarding_should_show'; | |
| 28 | + | |
| 29 | + | |
| 30 | + /** | |
| 20 | 31 | * Endpoint namespace. |
| 21 | 32 | * |
| 22 | 33 | * @var string |
| 23 | 34 | * @since 3.2.0 |
| @@ -181,8 +192,19 @@ | ||
| 181 | 192 | } |
| 182 | 193 | |
| 183 | 194 | |
| 184 | 195 | /** |
| 196 | + * Cache payload version. Bump whenever the shape or the meaning of a cached | |
| 197 | + * report response changes, so entries written by an older build are ignored | |
| 198 | + * instead of served until they expire. | |
| 199 | + * | |
| 200 | + * @var string | |
| 201 | + * @since 3.13.0 | |
| 202 | + */ | |
| 203 | + const CACHE_VERSION = 'v2'; | |
| 204 | + | |
| 205 | + | |
| 206 | + /** | |
| 185 | 207 | * Build a cache key from a prefix and parameter array. |
| 186 | 208 | * |
| 187 | 209 | * @param string $prefix |
| 188 | 210 | * @param array $params |
| @@ -190,9 +212,9 @@ | ||
| 190 | 212 | * |
| 191 | 213 | * @since 3.9.6 |
| 192 | 214 | */ |
| 193 | 215 | protected function get_cache_key( $prefix, $params ) { |
| 194 | - return 'wpfnl_dash_' . $prefix . '_' . md5( serialize( $params ) ); | |
| 216 | + return 'wpfnl_dash_' . $prefix . '_' . self::CACHE_VERSION . '_' . md5( serialize( $params ) ); | |
| 195 | 217 | } |
| 196 | 218 | |
| 197 | 219 | |
| 198 | 220 | /** |
| @@ -288,8 +310,31 @@ | ||
| 288 | 310 | } |
| 289 | 311 | |
| 290 | 312 | |
| 291 | 313 | /** |
| 314 | + * Remember whether the checklist was on show, for the next page load. | |
| 315 | + * | |
| 316 | + * Working this out costs several queries, so the admin page cannot ask | |
| 317 | + * before it prints — but it can read the last answer. The React dashboard | |
| 318 | + * uses it for one thing: whether to hold space for the checklist while the | |
| 319 | + * real status loads. Someone who is done with onboarding then sees no | |
| 320 | + * placeholder at all, instead of a skeleton that resolves to nothing. | |
| 321 | + * | |
| 322 | + * Short lived on purpose: it only has to outlive the gap between one page | |
| 323 | + * load and the next, and a stale answer costs at most one wrong placeholder. | |
| 324 | + * | |
| 325 | + * @param bool $is_visible Whether the checklist is on show. | |
| 326 | + * | |
| 327 | + * @return void | |
| 328 | + * @since 3.13.3 | |
| 329 | + */ | |
| 330 | + private function remember_onboarding_visibility( $is_visible ) | |
| 331 | + { | |
| 332 | + set_transient( self::ONBOARDING_VISIBILITY_KEY, $is_visible ? 'yes' : 'no', 15 * MINUTE_IN_SECONDS ); | |
| 333 | + } | |
| 334 | + | |
| 335 | + | |
| 336 | + /** | |
| 292 | 337 | * Get onboarding status. |
| 293 | 338 | * |
| 294 | 339 | * Determines whether the onboarding section should be shown on the dashboard. |
| 295 | 340 | * The onboarding is hidden when ALL three conditions are met: |
| @@ -306,8 +351,10 @@ | ||
| 306 | 351 | { |
| 307 | 352 | // ── Check if onboarding has been dismissed ── |
| 308 | 353 | $is_dismissed = get_transient( 'wpfnl_onboarding_dismissed' ); |
| 309 | 354 | if ( $is_dismissed ) { |
| 355 | + $this->remember_onboarding_visibility( false ); | |
| 356 | + | |
| 310 | 357 | return rest_ensure_response( array( |
| 311 | 358 | 'success' => true, |
| 312 | 359 | 'show_onboarding' => false, |
| 313 | 360 | 'steps' => array(), |
| @@ -427,8 +474,61 @@ | ||
| 427 | 474 | } |
| 428 | 475 | } |
| 429 | 476 | } |
| 430 | 477 | |
| 478 | + // ── Build the checklist deep links ── | |
| 479 | + // The checklist actions should drop the user into the place where the step | |
| 480 | + // actually gets done, rather than the funnel list: the order bump tab of the | |
| 481 | + // newest funnel's checkout step, and the canvas of that funnel for the upsell. | |
| 482 | + // Drafts count too — a funnel does not need to be live to be worked on. | |
| 483 | + $order_bump_url = ''; | |
| 484 | + $funnel_builder_url = ''; | |
| 485 | + $recent_funnels = get_posts(array( | |
| 486 | + 'post_type' => WPFNL_FUNNELS_POST_TYPE, | |
| 487 | + 'post_status' => array('publish', 'draft'), | |
| 488 | + 'numberposts' => 10, | |
| 489 | + 'orderby' => 'date', | |
| 490 | + 'order' => 'DESC', | |
| 491 | + 'fields' => 'ids', | |
| 492 | + 'meta_query' => $exclude_sc_meta, | |
| 493 | + )); | |
| 494 | + | |
| 495 | + if (!empty($recent_funnels)) { | |
| 496 | + // The upsell step is added on the canvas, so that is where its action goes. | |
| 497 | + $funnel_builder_url = admin_url( | |
| 498 | + sprintf('admin.php?page=edit_funnel&id=%d', $recent_funnels[0]) | |
| 499 | + ); | |
| 500 | + } | |
| 501 | + | |
| 502 | + foreach ($recent_funnels as $recent_funnel_id) { | |
| 503 | + $recent_steps = Wpfnl_functions::get_steps($recent_funnel_id); | |
| 504 | + | |
| 505 | + if (!is_array($recent_steps) || empty($recent_steps)) { | |
| 506 | + continue; | |
| 507 | + } | |
| 508 | + | |
| 509 | + foreach ($recent_steps as $recent_step) { | |
| 510 | + $recent_step_type = isset($recent_step['step_type']) ? $recent_step['step_type'] : ''; | |
| 511 | + $recent_step_id = isset($recent_step['id']) ? (int)$recent_step['id'] : 0; | |
| 512 | + | |
| 513 | + if ('checkout' !== $recent_step_type || !$recent_step_id) { | |
| 514 | + continue; | |
| 515 | + } | |
| 516 | + | |
| 517 | + // The hash is what tells the canvas which step settings drawer and | |
| 518 | + // tab to open once the funnel editor has finished loading. | |
| 519 | + $order_bump_url = admin_url( | |
| 520 | + sprintf( | |
| 521 | + 'admin.php?page=edit_funnel&id=%d&step_id=%d#%d/settings/order-bump/', | |
| 522 | + $recent_funnel_id, | |
| 523 | + $recent_step_id, | |
| 524 | + $recent_step_id | |
| 525 | + ) | |
| 526 | + ); | |
| 527 | + break 2; | |
| 528 | + } | |
| 529 | + } | |
| 530 | + | |
| 431 | 531 | // ── Step 3: Check for real orders through funnels ── |
| 432 | 532 | if ($is_wc_active && !empty($funnels)) { |
| 433 | 533 | global $wpdb; |
| 434 | 534 | $stats_table = $wpdb->prefix . 'wpfnl_stats'; |
| @@ -452,11 +552,10 @@ | ||
| 452 | 552 | |
| 453 | 553 | // ── Build checklist step completion data ── |
| 454 | 554 | $completed_count = 0; |
| 455 | 555 | $steps_status = array( |
| 456 | - 'woocommerce_connected' => $is_wc_active, | |
| 556 | + 'store_checkout_created' => $has_store_checkout_created, | |
| 457 | 557 | 'funnel_created' => $has_funnel_created, |
| 458 | - 'store_checkout_created' => $has_store_checkout_created, | |
| 459 | 558 | 'order_bump_added' => $has_order_bump, |
| 460 | 559 | 'upsell_added' => $has_upsell, |
| 461 | 560 | 'test_order_placed' => $has_real_order, |
| 462 | 561 | 'funnel_live' => $has_live_funnel, |
| @@ -470,8 +569,10 @@ | ||
| 470 | 569 | |
| 471 | 570 | $total_steps = count($steps_status); |
| 472 | 571 | $progress_percent = $total_steps > 0 ? round(($completed_count / $total_steps) * 100) : 0; |
| 473 | 572 | |
| 573 | + $this->remember_onboarding_visibility( !$should_hide_onboarding ); | |
| 574 | + | |
| 474 | 575 | return rest_ensure_response(array( |
| 475 | 576 | 'success' => true, |
| 476 | 577 | 'show_onboarding' => !$should_hide_onboarding, |
| 477 | 578 | 'steps' => $steps_status, |
| @@ -477,8 +578,10 @@ | ||
| 477 | 578 | 'steps' => $steps_status, |
| 478 | 579 | 'completed_count' => $completed_count, |
| 479 | 580 | 'total_steps' => $total_steps, |
| 480 | 581 | 'progress_percent' => $progress_percent, |
| 582 | + 'order_bump_url' => $order_bump_url, | |
| 583 | + 'funnel_builder_url' => $funnel_builder_url, | |
| 481 | 584 | )); |
| 482 | 585 | } |
| 483 | 586 | |
| 484 | 587 | /** |