| @@ -531,8 +531,13 @@ | ||
| 531 | 531 | // means the request-terminating nginx isn't user-editable on this |
| 532 | 532 | // host — the banner uses this to switch to honest messaging |
| 533 | 533 | // instead of dangling a snippet the user can't apply. |
| 534 | 534 | 'behind_proxy' => Server::is_behind_proxy(), |
| 535 | + // Mirrors /status (see the note there). On first paint the | |
| 536 | + // probe is whatever was last cached, so this is usually | |
| 537 | + // `unknown` until the Health tab runs a live probe — the | |
| 538 | + // dashboard needs the key present either way. | |
| 539 | + 'rules' => Cache::rules_state( $probe ), | |
| 535 | 540 | ); |
| 536 | 541 | } |
| 537 | 542 | |
| 538 | 543 | return array( |
| @@ -551,9 +556,18 @@ | ||
| 551 | 556 | // /status block so the dashboard callout renders on first paint |
| 552 | 557 | // without waiting for a status re-fetch. |
| 553 | 558 | 'mobile_separate' => array( |
| 554 | 559 | 'enabled' => (bool) ( $opts['cache_enabled'] ? ( Settings_Manager::get( 'cache' )['mobile_separate'] ?? false ) : false ), |
| 555 | - 'blocking' => $rewrite_capable && 'mobile_separate' === Cache::static_rewrite_block_reason(), | |
| 560 | + // Gated to servers that HAVE a static fast path — on IIS or | |
| 561 | + // an undetected server block_reason still falls through to | |
| 562 | + // mobile_separate, and reporting that as "blocking" would | |
| 563 | + // nag about a rewrite that does not exist there (#108). | |
| 564 | + // LiteSpeed joined the capable set with the Static Fast | |
| 565 | + // Path opt-in (#509); its own refusal (litespeed_dropin) | |
| 566 | + // outranks mobile_separate, so this stays false there | |
| 567 | + // until the opt-in is on. | |
| 568 | + 'blocking' => ( $rewrite_capable || Server::LITESPEED === $server_type ) | |
| 569 | + && 'mobile_separate' === Cache::static_rewrite_block_reason(), | |
| 556 | 570 | 'needs_review' => Cache::mobile_separate_needs_review(), |
| 557 | 571 | ), |
| 558 | 572 | // One consolidated nginx server-block snippet aggregating |
| 559 | 573 | // every enabled module's directives (Cache static-rewrite, |
| @@ -619,8 +633,13 @@ | ||
| 619 | 633 | if ( empty( $schema ) && empty( $custom_panel ) ) { |
| 620 | 634 | continue; |
| 621 | 635 | } |
| 622 | 636 | $settings = Settings_Manager::get_public( $slug ); |
| 637 | + // Something else on the site has taken over this module's job. It | |
| 638 | + // stands down whatever its own switch says, so it is reported off, | |
| 639 | + // with the reason in place of its usual one. | |
| 640 | + $blocked = $module->blocked_by(); | |
| 641 | + $active = $module->is_active(); | |
| 623 | 642 | |
| 624 | 643 | $entry = array( |
| 625 | 644 | 'slug' => $slug, |
| 626 | 645 | 'tier' => $module->tier(), |
| @@ -645,9 +664,9 @@ | ||
| 645 | 664 | // must read THIS, not `enabled`, which only ever described the |
| 646 | 665 | // modules that happen to store that one key. null means the |
| 647 | 666 | // module has no meaningful on/off and should be excluded from |
| 648 | 667 | // any count rather than treated as off. (#363) |
| 649 | - 'active' => $module->is_active(), | |
| 668 | + 'active' => ( null !== $blocked && true === $active ) ? false : $active, | |
| 650 | 669 | // One sentence explaining the line above, computed next to it |
| 651 | 670 | // so the two cannot disagree. The UI shows it behind an (i) |
| 652 | 671 | // beside the status pill: "On" is a bare assertion otherwise, |
| 653 | 672 | // and least obvious exactly where it matters -- Media |
| @@ -652,12 +671,23 @@ | ||
| 652 | 671 | // beside the status pill: "On" is a bare assertion otherwise, |
| 653 | 672 | // and least obvious exactly where it matters -- Media |
| 654 | 673 | // Optimization reads On while its two most prominent switches |
| 655 | 674 | // are off, because three other flags are on. (#363) |
| 656 | - 'active_reason' => $module->active_reason(), | |
| 675 | + 'active_reason' => null !== $blocked ? $blocked : $module->active_reason(), | |
| 676 | + // A purchase or licence the module still waits on. Shown in the | |
| 677 | + // page header in place of the On/Off pill. Not while another | |
| 678 | + // module has taken over: that reason is the one to show. | |
| 679 | + 'status_label' => null !== $blocked ? null : $module->status_label(), | |
| 657 | 680 | 'label' => $meta['label'] ?? ucfirst( $slug ), |
| 658 | 681 | 'icon' => $meta['icon'] ?? 'Square', |
| 659 | 682 | 'description' => $meta['description'] ?? '', |
| 683 | + // The dashboard group the module belongs to (cache, performance, | |
| 684 | + // network, insights, tools, ai-agents, settings), declared by the | |
| 685 | + // module itself. The Hub kept its own copy of this map, so every | |
| 686 | + // new module landed in its "Other" bucket until the Hub shipped. | |
| 687 | + // Must agree with src/components/sidebarGroups.ts; a unit test | |
| 688 | + // holds the two together. | |
| 689 | + 'group' => $meta['group'] ?? null, | |
| 660 | 690 | // Short label for the module's own tab when it hosts a tabbed |
| 661 | 691 | // page (FBS-83633). Only set on host modules. |
| 662 | 692 | 'tab_label' => $meta['tab_label'] ?? null, |
| 663 | 693 | // Public view: real values except secret fields, which are masked. |
| @@ -673,8 +703,12 @@ | ||
| 673 | 703 | 'setting_origins' => Settings_Manager::origins( $slug ), |
| 674 | 704 | 'schema' => $schema, |
| 675 | 705 | 'notices' => $module->ui_notices(), |
| 676 | 706 | 'custom_panel' => $meta['custom_panel'] ?? null, |
| 707 | + // Why this module may not be switched on, when something else | |
| 708 | + // on the site has taken over from it. Null for almost every | |
| 709 | + // module almost always — Free never blocks anything itself. | |
| 710 | + 'blocked_by' => $blocked, | |
| 677 | 711 | ); |
| 678 | 712 | |
| 679 | 713 | /** |
| 680 | 714 | * Last-mile descriptor filter. Lets Pro (or third-party |