From 128c63c39f6abe29628e1ef419d99342cfb2d534 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 2 Sep 2026 10:12:58 +0100 Subject: [PATCH 1/2] Allow static cache CS to be externalized --- config/static_caching.php | 17 ++++ .../views/static-caching/csrf-js.blade.php | 39 ++++++++ .../views/static-caching/nocache-js.blade.php | 51 ++++++++++ .../views/static-caching/script.blade.php | 13 +++ routes/web.php | 6 ++ src/Providers/AppServiceProvider.php | 4 + src/StaticCaching/Cachers/FileCacher.php | 98 ++----------------- .../NoCache/ScriptController.php | 37 +++++++ .../Replacers/CsrfTokenReplacer.php | 16 ++- .../Replacers/NoCacheReplacer.php | 15 ++- src/Testing/Concerns/FakesViews.php | 6 ++ .../ExternalScriptDeliveryTest.php | 76 ++++++++++++++ .../FullMeasureStaticCachingTest.php | 16 +++ tests/StaticCaching/NocacheRouteTest.php | 11 +++ 14 files changed, 308 insertions(+), 97 deletions(-) create mode 100644 resources/views/static-caching/csrf-js.blade.php create mode 100644 resources/views/static-caching/nocache-js.blade.php create mode 100644 resources/views/static-caching/script.blade.php create mode 100644 src/StaticCaching/NoCache/ScriptController.php create mode 100644 tests/StaticCaching/ExternalScriptDeliveryTest.php diff --git a/config/static_caching.php b/config/static_caching.php index 46766739505..2af89331ea4 100644 --- a/config/static_caching.php +++ b/config/static_caching.php @@ -142,6 +142,23 @@ \Statamic\StaticCaching\Replacers\NoCacheReplacer::class, ], + /* + |-------------------------------------------------------------------------- + | Script Delivery + |-------------------------------------------------------------------------- + | + | Full measure static caching injects small @else@endif diff --git a/routes/web.php b/routes/web.php index cb63cae04bd..8a947c7c485 100755 --- a/routes/web.php +++ b/routes/web.php @@ -34,6 +34,7 @@ use Statamic\StaticCaching\NoCache\CsrfTokenController; use Statamic\StaticCaching\NoCache\NoCacheController; use Statamic\StaticCaching\NoCache\NoCacheLocalize; +use Statamic\StaticCaching\NoCache\ScriptController; Route::name('statamic.')->group(function () { Route::group(['prefix' => config('statamic.routes.action')], function () { @@ -109,6 +110,11 @@ Route::post('csrf', CsrfTokenController::class) ->withoutMiddleware(['App\Http\Middleware\VerifyCsrfToken', 'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken', 'Illuminate\Foundation\Http\Middleware\PreventRequestForgery']); + if (config('statamic.static_caching.script_delivery') === 'external') { + Route::get('nocache.js', [ScriptController::class, 'nocache'])->name('nocache.js'); + Route::get('csrf.js', [ScriptController::class, 'csrf'])->name('csrf.js'); + } + Statamic::additionalActionRoutes(); }); diff --git a/src/Providers/AppServiceProvider.php b/src/Providers/AppServiceProvider.php index 05bb28b501f..f9be0a7e58e 100644 --- a/src/Providers/AppServiceProvider.php +++ b/src/Providers/AppServiceProvider.php @@ -105,6 +105,10 @@ public function boot() "{$this->root}/resources/views/extend/scaffolding" => resource_path('views/vendor/statamic/scaffolding'), ], 'statamic-scaffolding'); + $this->publishes([ + "{$this->root}/resources/views/static-caching" => resource_path('views/vendor/statamic/static-caching'), + ], 'statamic-static-caching'); + $this->app['redirect']->macro('cpRoute', function ($route, $parameters = []) { /** @var \Illuminate\Routing\Redirector $this */ return $this->to(cp_route($route, $parameters)); diff --git a/src/StaticCaching/Cachers/FileCacher.php b/src/StaticCaching/Cachers/FileCacher.php index 2b339e40120..66d56809b18 100644 --- a/src/StaticCaching/Cachers/FileCacher.php +++ b/src/StaticCaching/Cachers/FileCacher.php @@ -248,102 +248,16 @@ public function setNocacheJs(string $js) public function getCsrfTokenJs(): string { - $csrfPlaceholder = CsrfTokenReplacer::REPLACEMENT; - - $default = << response.json()) - .then((data) => { - for (const input of document.querySelectorAll('input[value="$csrfPlaceholder"]')) { - input.value = data.csrf; - } - - for (const meta of document.querySelectorAll('meta[content="$csrfPlaceholder"]')) { - meta.content = data.csrf; - } - - for (const input of document.querySelectorAll('script[data-csrf="$csrfPlaceholder"]')) { - input.setAttribute('data-csrf', data.csrf); - } - - if (window.hasOwnProperty('livewire_token')) { - window.livewire_token = data.csrf - } - - if (window.livewireScriptConfig) { - // Replaces token if Livewire is already available. Usually on fast networks. - window.livewireScriptConfig.csrf = data.csrf; - } else { - // Delays replacing the token until Livewire is initialized. Usually on slow networks. - document.addEventListener('livewire:init', () => window.livewireScriptConfig.csrf = data.csrf); - } - - document.dispatchEvent(new CustomEvent('statamic:csrf.replaced', { detail: data })); - }); -})(); -EOT; - - return $this->csrfTokenJs ?? $default; + return $this->csrfTokenJs ?? trim(view('statamic::static-caching.csrf-js', [ + 'csrfPlaceholder' => CsrfTokenReplacer::REPLACEMENT, + ])->render()); } public function getNocacheJs(): string { - $nocacheUrl = URL::makeRelative(route('statamic.nocache')); - - $default = << response.json()) - .then((data) => { - map = createMap(); - - const regions = data.regions; - for (var key in regions) { - if (map[key]) replaceElement(map[key], regions[key]); - } - - document.dispatchEvent(new CustomEvent('statamic:nocache.replaced', { detail: data })); - }); -})(); -EOT; - - return $this->nocacheJs ?? $default; + return $this->nocacheJs ?? trim(view('statamic::static-caching.nocache-js', [ + 'nocacheUrl' => URL::makeRelative(route('statamic.nocache')), + ])->render()); } public function shouldOutputJs(): bool diff --git a/src/StaticCaching/NoCache/ScriptController.php b/src/StaticCaching/NoCache/ScriptController.php new file mode 100644 index 00000000000..e3ec0a111b7 --- /dev/null +++ b/src/StaticCaching/NoCache/ScriptController.php @@ -0,0 +1,37 @@ +response($this->cacher()->getNocacheJs()); + } + + public function csrf(): Response + { + return $this->response($this->cacher()->getCsrfTokenJs()); + } + + private function cacher(): FileCacher + { + $cacher = app(Cacher::class); + + abort_unless($cacher instanceof FileCacher, 404); + + return $cacher; + } + + private function response(string $js): Response + { + return response($js) + ->header('Content-Type', 'application/javascript') + ->header('Cache-Control', 'public, max-age=3600') + ->setEtag(md5($js)); + } +} diff --git a/src/StaticCaching/Replacers/CsrfTokenReplacer.php b/src/StaticCaching/Replacers/CsrfTokenReplacer.php index 485b14564f1..054676ed6d3 100644 --- a/src/StaticCaching/Replacers/CsrfTokenReplacer.php +++ b/src/StaticCaching/Replacers/CsrfTokenReplacer.php @@ -4,6 +4,7 @@ use Illuminate\Http\Response; use Statamic\Facades\StaticCache; +use Statamic\Facades\URL; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\Cachers\FileCacher; use Statamic\StaticCaching\Replacer; @@ -81,10 +82,19 @@ private function modifyFullMeasureResponse(Response $response) Str::position($contents, ''), ])->filter()->min(); - $js = ""; - - $contents = Str::substrReplace($contents, $js, $insertBefore, 0); + $contents = Str::substrReplace($contents, $this->scriptTag($cacher), $insertBefore, 0); $response->setContent($contents); } + + private function scriptTag(FileCacher $cacher): string + { + $external = config('statamic.static_caching.script_delivery') === 'external'; + + return trim(view('statamic::static-caching.script', [ + 'inline' => ! $external, + 'src' => $external ? URL::makeRelative(route('statamic.csrf.js')) : null, + 'contents' => $external ? null : $cacher->getCsrfTokenJs(), + ])->render()); + } } diff --git a/src/StaticCaching/Replacers/NoCacheReplacer.php b/src/StaticCaching/Replacers/NoCacheReplacer.php index f7ca32fd97f..bae6743cc35 100644 --- a/src/StaticCaching/Replacers/NoCacheReplacer.php +++ b/src/StaticCaching/Replacers/NoCacheReplacer.php @@ -4,6 +4,7 @@ use Illuminate\Http\Response; use Statamic\Facades\StaticCache; +use Statamic\Facades\URL; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\Cachers\FileCacher; use Statamic\StaticCaching\NoCache\Session; @@ -94,12 +95,22 @@ private function modifyFullMeasureResponse(Response $response) $contents = $response->getContent(); if ($cacher->shouldOutputJs()) { - $js = $cacher->getNocacheJs(); - $contents = str_replace('', '', $contents); + $contents = str_replace('', $this->scriptTag($cacher).'', $contents); } $contents = str_replace('NOCACHE_PLACEHOLDER', $cacher->getNocachePlaceholder(), $contents); $response->setContent($contents); } + + private function scriptTag(FileCacher $cacher): string + { + $external = config('statamic.static_caching.script_delivery') === 'external'; + + return trim(view('statamic::static-caching.script', [ + 'inline' => ! $external, + 'src' => $external ? URL::makeRelative(route('statamic.nocache.js')) : null, + 'contents' => $external ? null : $cacher->getNocacheJs(), + ])->render()); + } } diff --git a/src/Testing/Concerns/FakesViews.php b/src/Testing/Concerns/FakesViews.php index a37766a11dc..38cd72597d4 100644 --- a/src/Testing/Concerns/FakesViews.php +++ b/src/Testing/Concerns/FakesViews.php @@ -19,6 +19,12 @@ public function withFakeViews() $this->fakeView = app(FakeViewEngine::class); $this->fakeViewFinder = new FakeViewFinder($this->app['files'], config('view.paths')); + // Keep real namespace hints (e.g. `statamic::`) resolving so that faking + // the frontend views doesn't break package views rendered as a side effect. + foreach ($originalFactory->getFinder()->getHints() as $namespace => $paths) { + $this->fakeViewFinder->addNamespace($namespace, $paths); + } + $this->fakeViewFactory = new FakeViewFactory($this->app['view.engine.resolver'], $this->fakeViewFinder, $this->app['events']); $this->fakeViewFactory->setFakeEngine($this->fakeView); foreach (array_reverse($originalFactory->getExtensions()) as $ext => $engine) { diff --git a/tests/StaticCaching/ExternalScriptDeliveryTest.php b/tests/StaticCaching/ExternalScriptDeliveryTest.php new file mode 100644 index 00000000000..6884c2205ae --- /dev/null +++ b/tests/StaticCaching/ExternalScriptDeliveryTest.php @@ -0,0 +1,76 @@ +set('statamic.static_caching.strategy', 'full'); + $app['config']->set('statamic.static_caching.strategies.full.path', $this->dir = __DIR__.'/static'); + $app['config']->set('statamic.static_caching.script_delivery', 'external'); + + File::delete($this->dir); + } + + public function tearDown(): void + { + File::delete($this->dir); + parent::tearDown(); + } + + #[Test] + public function it_references_the_csrf_and_nocache_scripts_instead_of_inlining_them() + { + $this->withFakeViews(); + $this->viewShouldReturnRaw('layout', '{{ template_content }}'); + $this->viewShouldReturnRaw('default', '{{ csrf_token }}'); + + $this->createPage('about'); + + $expected = 'STATAMIC_CSRF_TOKEN'; + + $response = $this->get('/about')->assertOk(); + + $this->assertEquals($expected, $response->getContent()); + $this->assertStringNotContainsString('(function()', $response->getContent()); + $this->assertEquals($expected, file_get_contents($this->dir.'/about_.html')); + } + + #[Test] + public function the_scripts_are_served_from_routes() + { + $nocache = $this->get('/!/nocache.js')->assertOk(); + $this->assertStringContainsString('application/javascript', $nocache->headers->get('content-type')); + $this->assertEquals(app(Cacher::class)->getNocacheJs(), $nocache->getContent()); + $this->assertStringContainsString("fetch('/!/nocache'", $nocache->getContent()); + + $csrf = $this->get('/!/csrf.js')->assertOk(); + $this->assertStringContainsString('application/javascript', $csrf->headers->get('content-type')); + $this->assertEquals(app(Cacher::class)->getCsrfTokenJs(), $csrf->getContent()); + } + + #[Test] + public function the_routes_are_registered_only_in_external_mode() + { + $this->assertTrue(Route::has('statamic.nocache.js')); + $this->assertTrue(Route::has('statamic.csrf.js')); + } +} diff --git a/tests/StaticCaching/FullMeasureStaticCachingTest.php b/tests/StaticCaching/FullMeasureStaticCachingTest.php index 243586f4c14..641e1913cc1 100644 --- a/tests/StaticCaching/FullMeasureStaticCachingTest.php +++ b/tests/StaticCaching/FullMeasureStaticCachingTest.php @@ -176,6 +176,22 @@ public function it_can_override_the_csrf_and_nocache_scripts() $this->assertEquals(app(Cacher::class)->getCsrfTokenJs(), 'csrf'); } + #[Test] + public function the_injected_script_tag_comes_from_a_publishable_view() + { + // Inline: the body is embedded. + $this->assertEquals( + '', + trim(view('statamic::static-caching.script', ['inline' => true, 'src' => null, 'contents' => 'alert(1)'])->render()) + ); + + // External: the body is referenced. + $this->assertEquals( + '', + trim(view('statamic::static-caching.script', ['inline' => false, 'src' => '/foo.js', 'contents' => null])->render()) + ); + } + #[Test] public function excluded_pages_should_have_real_csrf_token() { diff --git a/tests/StaticCaching/NocacheRouteTest.php b/tests/StaticCaching/NocacheRouteTest.php index f9ae48ecd34..f87de014120 100644 --- a/tests/StaticCaching/NocacheRouteTest.php +++ b/tests/StaticCaching/NocacheRouteTest.php @@ -61,4 +61,15 @@ public function url_is_required() ->postJson('/!/nocache') ->assertJsonValidationErrorFor('url'); } + + #[Test] + public function the_script_routes_are_not_registered_unless_script_delivery_is_external() + { + // Defaults to "inline", so the routes shouldn't exist. + $this->assertFalse(\Illuminate\Support\Facades\Route::has('statamic.nocache.js')); + $this->assertFalse(\Illuminate\Support\Facades\Route::has('statamic.csrf.js')); + + $this->get('/!/nocache.js')->assertNotFound(); + $this->get('/!/csrf.js')->assertNotFound(); + } } From ebae1f2a99244110d9288ba8acf16e4ed6f6bea9 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 18 Sep 2026 16:09:23 -0400 Subject: [PATCH 2/2] Build the script tags in PHP rather than publishable views The views were publishable, so a site could freeze a copy of the CSRF and nocache JS and silently miss future fixes to it. The publish tag copies the whole directory, so someone who only wanted to change the script tag would have frozen both JS bodies too. The tag itself also shouldn't have been a view: it re-branched on inline vs external, a decision the config already makes, so a published copy could contradict the config and break the mode it wasn't trying to touch. The nonce example that justified publishing doesn't hold either. These tags are injected into full measure responses only, and those get written to disk and replayed, so a per-request nonce would be frozen and reused for every visitor. External delivery is the answer for a strict policy, which is what this PR is for. So FileCacher builds the tags itself and owns inline vs external in one place, and both replacers just ask it for one. The default JS stays in the heredocs it was already in, leaving FileCacher purely additive. StaticCache::csrfTokenJs() and nocacheJs() still replace the bodies outright. Also reverts the FakesViews namespace hint fix, which was only needed because the replacers rendered a statamic:: view. Co-Authored-By: Claude Opus 5 --- .../views/static-caching/csrf-js.blade.php | 39 ------ .../views/static-caching/nocache-js.blade.php | 51 -------- .../views/static-caching/script.blade.php | 13 -- src/Providers/AppServiceProvider.php | 4 - src/StaticCaching/Cachers/FileCacher.php | 116 +++++++++++++++++- .../Replacers/CsrfTokenReplacer.php | 14 +-- .../Replacers/NoCacheReplacer.php | 14 +-- src/Testing/Concerns/FakesViews.php | 6 - .../FullMeasureStaticCachingTest.php | 16 --- 9 files changed, 112 insertions(+), 161 deletions(-) delete mode 100644 resources/views/static-caching/csrf-js.blade.php delete mode 100644 resources/views/static-caching/nocache-js.blade.php delete mode 100644 resources/views/static-caching/script.blade.php diff --git a/resources/views/static-caching/csrf-js.blade.php b/resources/views/static-caching/csrf-js.blade.php deleted file mode 100644 index 5e728c9bc2a..00000000000 --- a/resources/views/static-caching/csrf-js.blade.php +++ /dev/null @@ -1,39 +0,0 @@ -{{-- -Swaps the placeholder CSRF token in full-measure statically cached pages for a -real one. Injected inline by default, or served from a dedicated route when -static_caching.script_delivery is "external". Blade data: $csrfPlaceholder ---}} -(function() { - fetch('/!/csrf', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }) - .then((response) => response.json()) - .then((data) => { - for (const input of document.querySelectorAll('input[value="{{ $csrfPlaceholder }}"]')) { - input.value = data.csrf; - } - - for (const meta of document.querySelectorAll('meta[content="{{ $csrfPlaceholder }}"]')) { - meta.content = data.csrf; - } - - for (const input of document.querySelectorAll('script[data-csrf="{{ $csrfPlaceholder }}"]')) { - input.setAttribute('data-csrf', data.csrf); - } - - if (window.hasOwnProperty('livewire_token')) { - window.livewire_token = data.csrf - } - - if (window.livewireScriptConfig) { - // Replaces token if Livewire is already available. Usually on fast networks. - window.livewireScriptConfig.csrf = data.csrf; - } else { - // Delays replacing the token until Livewire is initialized. Usually on slow networks. - document.addEventListener('livewire:init', () => window.livewireScriptConfig.csrf = data.csrf); - } - - document.dispatchEvent(new CustomEvent('statamic:csrf.replaced', { detail: data })); - }); -})(); diff --git a/resources/views/static-caching/nocache-js.blade.php b/resources/views/static-caching/nocache-js.blade.php deleted file mode 100644 index 570787befbb..00000000000 --- a/resources/views/static-caching/nocache-js.blade.php +++ /dev/null @@ -1,51 +0,0 @@ -{{-- -Hydrates `nocache` regions in full-measure statically cached pages by fetching -their rendered contents. Injected inline by default, or served from a dedicated -route when static_caching.script_delivery is "external". Blade data: $nocacheUrl ---}} -(function() { - function createMap() { - var map = {}; - var els = document.getElementsByClassName('nocache'); - for (var i = 0; i < els.length; i++) { - var section = els[i].getAttribute('data-nocache'); - map[section] = els[i]; - } - return map; - } - - function replaceElement(el, html) { - const tmp = document.createElement('div'); - const fragment = document.createDocumentFragment(); - - tmp.setHTMLUnsafe(html); - - while (tmp.firstChild) { - fragment.appendChild(tmp.firstChild); - } - - el.replaceWith(fragment); - } - - var map = createMap(); - - fetch('{{ $nocacheUrl }}', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - url: window.location.href.split('#')[0], - sections: Object.keys(map) - }) - }) - .then((response) => response.json()) - .then((data) => { - map = createMap(); - - const regions = data.regions; - for (var key in regions) { - if (map[key]) replaceElement(map[key], regions[key]); - } - - document.dispatchEvent(new CustomEvent('statamic:nocache.replaced', { detail: data })); - }); -})(); diff --git a/resources/views/static-caching/script.blade.php b/resources/views/static-caching/script.blade.php deleted file mode 100644 index bc87753c59d..00000000000 --- a/resources/views/static-caching/script.blade.php +++ /dev/null @@ -1,13 +0,0 @@ -{{-- - The @else@endif diff --git a/src/Providers/AppServiceProvider.php b/src/Providers/AppServiceProvider.php index f9be0a7e58e..05bb28b501f 100644 --- a/src/Providers/AppServiceProvider.php +++ b/src/Providers/AppServiceProvider.php @@ -105,10 +105,6 @@ public function boot() "{$this->root}/resources/views/extend/scaffolding" => resource_path('views/vendor/statamic/scaffolding'), ], 'statamic-scaffolding'); - $this->publishes([ - "{$this->root}/resources/views/static-caching" => resource_path('views/vendor/statamic/static-caching'), - ], 'statamic-static-caching'); - $this->app['redirect']->macro('cpRoute', function ($route, $parameters = []) { /** @var \Illuminate\Routing\Redirector $this */ return $this->to(cp_route($route, $parameters)); diff --git a/src/StaticCaching/Cachers/FileCacher.php b/src/StaticCaching/Cachers/FileCacher.php index 66d56809b18..a7deaecfd67 100644 --- a/src/StaticCaching/Cachers/FileCacher.php +++ b/src/StaticCaching/Cachers/FileCacher.php @@ -2,6 +2,7 @@ namespace Statamic\StaticCaching\Cachers; +use Closure; use Illuminate\Contracts\Cache\Repository; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -248,16 +249,119 @@ public function setNocacheJs(string $js) public function getCsrfTokenJs(): string { - return $this->csrfTokenJs ?? trim(view('statamic::static-caching.csrf-js', [ - 'csrfPlaceholder' => CsrfTokenReplacer::REPLACEMENT, - ])->render()); + $csrfPlaceholder = CsrfTokenReplacer::REPLACEMENT; + + $default = << response.json()) + .then((data) => { + for (const input of document.querySelectorAll('input[value="$csrfPlaceholder"]')) { + input.value = data.csrf; + } + + for (const meta of document.querySelectorAll('meta[content="$csrfPlaceholder"]')) { + meta.content = data.csrf; + } + + for (const input of document.querySelectorAll('script[data-csrf="$csrfPlaceholder"]')) { + input.setAttribute('data-csrf', data.csrf); + } + + if (window.hasOwnProperty('livewire_token')) { + window.livewire_token = data.csrf + } + + if (window.livewireScriptConfig) { + // Replaces token if Livewire is already available. Usually on fast networks. + window.livewireScriptConfig.csrf = data.csrf; + } else { + // Delays replacing the token until Livewire is initialized. Usually on slow networks. + document.addEventListener('livewire:init', () => window.livewireScriptConfig.csrf = data.csrf); + } + + document.dispatchEvent(new CustomEvent('statamic:csrf.replaced', { detail: data })); + }); +})(); +EOT; + + return $this->csrfTokenJs ?? $default; } public function getNocacheJs(): string { - return $this->nocacheJs ?? trim(view('statamic::static-caching.nocache-js', [ - 'nocacheUrl' => URL::makeRelative(route('statamic.nocache')), - ])->render()); + $nocacheUrl = URL::makeRelative(route('statamic.nocache')); + + $default = << response.json()) + .then((data) => { + map = createMap(); + + const regions = data.regions; + for (var key in regions) { + if (map[key]) replaceElement(map[key], regions[key]); + } + + document.dispatchEvent(new CustomEvent('statamic:nocache.replaced', { detail: data })); + }); +})(); +EOT; + + return $this->nocacheJs ?? $default; + } + + public function getCsrfScript(): string + { + return $this->script('statamic.csrf.js', fn () => $this->getCsrfTokenJs()); + } + + public function getNocacheScript(): string + { + return $this->script('statamic.nocache.js', fn () => $this->getNocacheJs()); + } + + private function script(string $route, Closure $js): string + { + return config('statamic.static_caching.script_delivery') === 'external' + ? '' + : ''; } public function shouldOutputJs(): bool diff --git a/src/StaticCaching/Replacers/CsrfTokenReplacer.php b/src/StaticCaching/Replacers/CsrfTokenReplacer.php index 054676ed6d3..27cb0f8cc0d 100644 --- a/src/StaticCaching/Replacers/CsrfTokenReplacer.php +++ b/src/StaticCaching/Replacers/CsrfTokenReplacer.php @@ -4,7 +4,6 @@ use Illuminate\Http\Response; use Statamic\Facades\StaticCache; -use Statamic\Facades\URL; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\Cachers\FileCacher; use Statamic\StaticCaching\Replacer; @@ -82,19 +81,8 @@ private function modifyFullMeasureResponse(Response $response) Str::position($contents, ''), ])->filter()->min(); - $contents = Str::substrReplace($contents, $this->scriptTag($cacher), $insertBefore, 0); + $contents = Str::substrReplace($contents, $cacher->getCsrfScript(), $insertBefore, 0); $response->setContent($contents); } - - private function scriptTag(FileCacher $cacher): string - { - $external = config('statamic.static_caching.script_delivery') === 'external'; - - return trim(view('statamic::static-caching.script', [ - 'inline' => ! $external, - 'src' => $external ? URL::makeRelative(route('statamic.csrf.js')) : null, - 'contents' => $external ? null : $cacher->getCsrfTokenJs(), - ])->render()); - } } diff --git a/src/StaticCaching/Replacers/NoCacheReplacer.php b/src/StaticCaching/Replacers/NoCacheReplacer.php index bae6743cc35..3d67e3711e2 100644 --- a/src/StaticCaching/Replacers/NoCacheReplacer.php +++ b/src/StaticCaching/Replacers/NoCacheReplacer.php @@ -4,7 +4,6 @@ use Illuminate\Http\Response; use Statamic\Facades\StaticCache; -use Statamic\Facades\URL; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\Cachers\FileCacher; use Statamic\StaticCaching\NoCache\Session; @@ -95,22 +94,11 @@ private function modifyFullMeasureResponse(Response $response) $contents = $response->getContent(); if ($cacher->shouldOutputJs()) { - $contents = str_replace('', $this->scriptTag($cacher).'', $contents); + $contents = str_replace('', $cacher->getNocacheScript().'', $contents); } $contents = str_replace('NOCACHE_PLACEHOLDER', $cacher->getNocachePlaceholder(), $contents); $response->setContent($contents); } - - private function scriptTag(FileCacher $cacher): string - { - $external = config('statamic.static_caching.script_delivery') === 'external'; - - return trim(view('statamic::static-caching.script', [ - 'inline' => ! $external, - 'src' => $external ? URL::makeRelative(route('statamic.nocache.js')) : null, - 'contents' => $external ? null : $cacher->getNocacheJs(), - ])->render()); - } } diff --git a/src/Testing/Concerns/FakesViews.php b/src/Testing/Concerns/FakesViews.php index 38cd72597d4..a37766a11dc 100644 --- a/src/Testing/Concerns/FakesViews.php +++ b/src/Testing/Concerns/FakesViews.php @@ -19,12 +19,6 @@ public function withFakeViews() $this->fakeView = app(FakeViewEngine::class); $this->fakeViewFinder = new FakeViewFinder($this->app['files'], config('view.paths')); - // Keep real namespace hints (e.g. `statamic::`) resolving so that faking - // the frontend views doesn't break package views rendered as a side effect. - foreach ($originalFactory->getFinder()->getHints() as $namespace => $paths) { - $this->fakeViewFinder->addNamespace($namespace, $paths); - } - $this->fakeViewFactory = new FakeViewFactory($this->app['view.engine.resolver'], $this->fakeViewFinder, $this->app['events']); $this->fakeViewFactory->setFakeEngine($this->fakeView); foreach (array_reverse($originalFactory->getExtensions()) as $ext => $engine) { diff --git a/tests/StaticCaching/FullMeasureStaticCachingTest.php b/tests/StaticCaching/FullMeasureStaticCachingTest.php index 641e1913cc1..243586f4c14 100644 --- a/tests/StaticCaching/FullMeasureStaticCachingTest.php +++ b/tests/StaticCaching/FullMeasureStaticCachingTest.php @@ -176,22 +176,6 @@ public function it_can_override_the_csrf_and_nocache_scripts() $this->assertEquals(app(Cacher::class)->getCsrfTokenJs(), 'csrf'); } - #[Test] - public function the_injected_script_tag_comes_from_a_publishable_view() - { - // Inline: the body is embedded. - $this->assertEquals( - '', - trim(view('statamic::static-caching.script', ['inline' => true, 'src' => null, 'contents' => 'alert(1)'])->render()) - ); - - // External: the body is referenced. - $this->assertEquals( - '', - trim(view('statamic::static-caching.script', ['inline' => false, 'src' => '/foo.js', 'contents' => null])->render()) - ); - } - #[Test] public function excluded_pages_should_have_real_csrf_token() {