| @@ -560,8 +560,14 @@ | ||
| 560 | 560 | // BrowserCache headers, GZIP, …). Null on non-nginx hosts |
| 561 | 561 | // or when no module contributes directives. Replaces the |
| 562 | 562 | // per-module "paste this snippet" notices. |
| 563 | 563 | 'nginx_server_block' => Cache::full_nginx_server_block(), |
| 564 | + // Mirrors the /status block so the enable-time disclosure is | |
| 565 | + // correct on FIRST PAINT. Without it the top-bar switch can be | |
| 566 | + // clicked before a status fetch lands, and the one moment the | |
| 567 | + // warning exists for — a leftover drop-in about to be | |
| 568 | + // replaced — is exactly when it would be missing. | |
| 569 | + 'dropin' => Page_Cache_Detector::dropin_disclosure(), | |
| 564 | 570 | ), |
| 565 | 571 | // Registered Modules (Free + Pro). The React app discovers them |
| 566 | 572 | // here and renders one sidebar item + one panel per module that |
| 567 | 573 | // declares a settings schema. Hidden modules are filtered. |
| @@ -612,12 +618,43 @@ | ||
| 612 | 618 | // panel — i.e., truly nothing to render in the dashboard. |
| 613 | 619 | if ( empty( $schema ) && empty( $custom_panel ) ) { |
| 614 | 620 | continue; |
| 615 | 621 | } |
| 622 | + $settings = Settings_Manager::get_public( $slug ); | |
| 623 | + | |
| 616 | 624 | $entry = array( |
| 617 | 625 | 'slug' => $slug, |
| 618 | 626 | 'tier' => $module->tier(), |
| 619 | 627 | 'version' => $module->version(), |
| 628 | + // Promoted from inside `settings` so the payload is | |
| 629 | + // self-describing. Consumers kept tripping on this — the Hub | |
| 630 | + // rendered every module "Inactive" until it learned to look | |
| 631 | + // inside the settings bag. `settings.enabled` is kept below | |
| 632 | + // for back-compat; this is the same value, not a second | |
| 633 | + // source of truth. Modules with no `enabled` key (status | |
| 634 | + // panels like Health) report null rather than a misleading | |
| 635 | + // false. (#146) | |
| 636 | + 'enabled' => array_key_exists( 'enabled', $settings ) | |
| 637 | + ? (bool) $settings['enabled'] | |
| 638 | + : null, | |
| 639 | + // Whether the module is actually DOING something, which is not | |
| 640 | + // the same question as `enabled` above. "On" has several shapes | |
| 641 | + // -- page caching lives in the global option, Minify and Lazy | |
| 642 | + // are on when any flag is set, MCP when it is connected -- so | |
| 643 | + // each module answers for itself via is_active(). Consumers | |
| 644 | + // that want "what is switched on?" (the sidebar's "N on" badge) | |
| 645 | + // must read THIS, not `enabled`, which only ever described the | |
| 646 | + // modules that happen to store that one key. null means the | |
| 647 | + // module has no meaningful on/off and should be excluded from | |
| 648 | + // any count rather than treated as off. (#363) | |
| 649 | + 'active' => $module->is_active(), | |
| 650 | + // One sentence explaining the line above, computed next to it | |
| 651 | + // so the two cannot disagree. The UI shows it behind an (i) | |
| 652 | + // beside the status pill: "On" is a bare assertion otherwise, | |
| 653 | + // and least obvious exactly where it matters -- Media | |
| 654 | + // Optimization reads On while its two most prominent switches | |
| 655 | + // are off, because three other flags are on. (#363) | |
| 656 | + 'active_reason' => $module->active_reason(), | |
| 620 | 657 | 'label' => $meta['label'] ?? ucfirst( $slug ), |
| 621 | 658 | 'icon' => $meta['icon'] ?? 'Square', |
| 622 | 659 | 'description' => $meta['description'] ?? '', |
| 623 | 660 | // Short label for the module's own tab when it hosts a tabbed |
| @@ -625,9 +662,16 @@ | ||
| 625 | 662 | 'tab_label' => $meta['tab_label'] ?? null, |
| 626 | 663 | // Public view: real values except secret fields, which are masked. |
| 627 | 664 | // The dashboard bundle localizes this into page HTML, so a raw |
| 628 | 665 | // credential here would be readable from view-source. (#115) |
| 629 | - 'settings' => Settings_Manager::get_public( $slug ), | |
| 666 | + 'settings' => $settings, | |
| 667 | + // Where each value actually came from: a wp-config.php constant, | |
| 668 | + // the option row, or the schema default. The panel renders a | |
| 669 | + // constant-sourced field read-only and names the constant, so it | |
| 670 | + // can never present an editable box over a value the site is not | |
| 671 | + // using. Every module gets this, not just the ones that declare | |
| 672 | + // constants today. (#398) | |
| 673 | + 'setting_origins' => Settings_Manager::origins( $slug ), | |
| 630 | 674 | 'schema' => $schema, |
| 631 | 675 | 'notices' => $module->ui_notices(), |
| 632 | 676 | 'custom_panel' => $meta['custom_panel'] ?? null, |
| 633 | 677 | ); |