| @@ -425,8 +425,13 @@ | ||
| 425 | 425 | 'nginx_snippet' => Gzip::nginx_snippet(), |
| 426 | 426 | ), |
| 427 | 427 | 'rewrite_probe' => $rewrite_probe, |
| 428 | 428 | 'nginx_server_block' => Cache::full_nginx_server_block(), |
| 429 | + // What enabling the page cache would do to | |
| 430 | + // wp-content/advanced-cache.php. The dashboard discloses the | |
| 431 | + // replacement BEFORE the write when a leftover drop-in is | |
| 432 | + // already there; see Page_Cache_Detector::dropin_disclosure(). | |
| 433 | + 'dropin' => Page_Cache_Detector::dropin_disclosure(), | |
| 429 | 434 | // Separate Mobile Cache visibility (FBS-83145). `blocking` is |
| 430 | 435 | // true when mobile_separate is what's keeping the device-blind |
| 431 | 436 | // static fast path from installing on a rewrite-capable server; |
| 432 | 437 | // `needs_review` is true when a migration turned it on for us and |
| @@ -459,10 +464,19 @@ | ||
| 459 | 464 | return rest_ensure_response( $updated ); |
| 460 | 465 | } |
| 461 | 466 | |
| 462 | 467 | public function purge() { |
| 463 | - Cache::purge_all( __( 'dashboard', 'xspeed' ) ); | |
| 464 | - return rest_ensure_response( array( 'stats' => Cache::get_stats() ) ); | |
| 468 | + // The same core function `wp xspeed purge` runs, so the dashboard | |
| 469 | + // button and the CLI cannot clear different sets of stores — and the | |
| 470 | + // per-store report is available here for the UI to surface a store | |
| 471 | + // that was skipped or refused rather than flashing "cache cleared". | |
| 472 | + $report = Purge_Runner::run( array( 'all' ), __( 'dashboard', 'xspeed' ) ); | |
| 473 | + return rest_ensure_response( | |
| 474 | + array( | |
| 475 | + 'stats' => Cache::get_stats(), | |
| 476 | + 'report' => $report, | |
| 477 | + ) | |
| 478 | + ); | |
| 465 | 479 | } |
| 466 | 480 | |
| 467 | 481 | /** |
| 468 | 482 | * The "Cached Pages" drill-down: which pages are cached and how old they |
| @@ -504,17 +518,27 @@ | ||
| 504 | 518 | * Returns the same shape the dashboard bootstrap uses, so the caller can |
| 505 | 519 | * swap it straight into state without a second round trip. (FBS-84012) |
| 506 | 520 | */ |
| 507 | 521 | public function recheck_rewrite() { |
| 508 | - $probe = Cache::recheck_static_rewrite(); | |
| 522 | + $raw = Cache::recheck_static_rewrite(); | |
| 509 | 523 | $server_type = Server::detect(); |
| 510 | 524 | |
| 525 | + // Qualify the raw probe against known config refusals. The probe | |
| 526 | + // fetches its own file from the static tree, which succeeds even when | |
| 527 | + // no real page is served that way — so an unqualified `active` told | |
| 528 | + // clients the static path was engaged on sites where it demonstrably | |
| 529 | + // wasn't. `block_reason` is exposed so a client can act on the | |
| 530 | + // specific cause rather than re-deriving it. See | |
| 531 | + // Cache::qualify_rewrite_probe(). | |
| 532 | + $probe = Cache::qualify_rewrite_probe( $raw ); | |
| 533 | + | |
| 511 | 534 | return rest_ensure_response( |
| 512 | 535 | array( |
| 513 | - 'active' => (bool) ( $probe['active'] ?? false ), | |
| 514 | - 'pending' => (bool) ( $probe['pending'] ?? false ), | |
| 515 | - 'inconclusive' => (bool) ( $probe['inconclusive'] ?? false ), | |
| 516 | - 'reason' => (string) ( $probe['reason'] ?? '' ), | |
| 536 | + 'active' => $probe['active'], | |
| 537 | + 'pending' => (bool) ( $raw['pending'] ?? false ), | |
| 538 | + 'inconclusive' => $probe['inconclusive'], | |
| 539 | + 'reason' => $probe['reason'], | |
| 540 | + 'block_reason' => $probe['block_reason'], | |
| 517 | 541 | 'server_type' => $server_type, |
| 518 | 542 | 'snippet' => Cache::nginx_snippet(), |
| 519 | 543 | 'topology' => Server::rewrite_topology(), |
| 520 | 544 | 'behind_proxy' => Server::is_behind_proxy(), |
| @@ -530,9 +554,9 @@ | ||
| 530 | 554 | // permission_callback above already enforced current_user_can( |
| 531 | 555 | // 'manage_options' ); the REST nonce is verified by core via the |
| 532 | 556 | // X-WP-Nonce header. |
| 533 | 557 | $state = Cache::toggle( $enabled ); |
| 534 | - $updated = Settings::update( array( 'cache_enabled' => $state['enabled'] ) ); | |
| 558 | + $updated = Settings::get(); | |
| 535 | 559 | |
| 536 | 560 | // Recompute the unified nginx block AFTER cache_enabled is persisted. |
| 537 | 561 | // Cache::toggle() computes it inline, but cache_enabled isn't written |
| 538 | 562 | // until the Settings::update() above — so the block inside $state |
| @@ -542,11 +566,17 @@ | ||
| 542 | 566 | $state['nginx_server_block'] = Cache::full_nginx_server_block(); |
| 543 | 567 | |
| 544 | 568 | return rest_ensure_response( |
| 545 | 569 | array( |
| 546 | - 'enabled' => $updated['cache_enabled'], | |
| 547 | - 'stats' => Cache::get_stats(), | |
| 548 | - 'install_state' => $state, | |
| 570 | + 'enabled' => $updated['cache_enabled'], | |
| 571 | + // Surfaced at the top level so the dashboard can explain a | |
| 572 | + // refusal rather than silently snapping the toggle back: | |
| 573 | + // Cache::toggle() writes nothing when another plugin owns the | |
| 574 | + // drop-in or WP_CACHE is in a shape we must not rewrite. | |
| 575 | + 'blocked' => ! empty( $state['blocked'] ), | |
| 576 | + 'blocked_reason' => $state['blocked_reason'] ?? null, | |
| 577 | + 'stats' => Cache::get_stats(), | |
| 578 | + 'install_state' => $state, | |
| 549 | 579 | ) |
| 550 | 580 | ); |
| 551 | 581 | } |
| 552 | 582 | } |