Hotfix: Vorschau im Inhalte-Editor und Seitennavigation der Medienbibliothek
- AllowPortalFraming: frame-ancestors 'self' + Portal statt X-Frame-Options SAMEORIGIN, damit portal.b2in.eu die Website im Vorschau-iframe zeigen darf - Medienbibliothek und Hub-Verwaltung: WithPagination – ohne zeigten die Seitenlinks nach dem ersten Livewire-Klick auf den Update-Endpunkt (405); Suche/Filter springen auf Seite 1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a661a68913
commit
6f1b27371b
5 changed files with 92 additions and 2 deletions
36
app/Http/Middleware/AllowPortalFraming.php
Normal file
36
app/Http/Middleware/AllowPortalFraming.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Erlaubt die Live-Vorschau im Inhalte-Editor des Portals (09.10.2026).
|
||||
*
|
||||
* Der Editor auf portal.b2in.eu zeigt die Website in einem iframe. Der Webserver schickt
|
||||
* `X-Frame-Options: SAMEORIGIN`, das jede andere Domain ausschließt – also auch das Portal.
|
||||
* `Content-Security-Policy: frame-ancestors` löst X-Frame-Options ab: Browser werten dann nur
|
||||
* noch diese Liste aus. Erlaubt sind die eigene Domain und das Portal, alle anderen Seiten
|
||||
* dürfen die Website weiterhin nicht einbetten.
|
||||
*/
|
||||
class AllowPortalFraming
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
if ($response->headers->has('Content-Security-Policy')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$portal = rtrim((string) config('domains.domain_portal_url'), '/');
|
||||
$ancestors = trim("'self' {$portal}");
|
||||
|
||||
$response->headers->set('Content-Security-Policy', "frame-ancestors {$ancestors}");
|
||||
$response->headers->remove('X-Frame-Options');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
// global, weil 404 vor dem Routing entsteht
|
||||
$middleware->append(ReportErrorSignals::class);
|
||||
|
||||
// Inhalte-Editor im Portal zeigt die Website im iframe: frame-ancestors statt SAMEORIGIN
|
||||
$middleware->append(\App\Http\Middleware\AllowPortalFraming::class);
|
||||
|
||||
// BasicAuth ganz am Ende, nach Session-Middleware
|
||||
if (env('BASIC_AUTH_ENABLED', true)) {
|
||||
$middleware->append(\App\Http\Middleware\BasicAuthMiddleware::class);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ use Flux\Flux;
|
|||
use FluxCms\Core\Models\CmsMedia;
|
||||
use FluxCms\Core\Services\MediaConversionService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use function Livewire\Volt\{layout, title, state, computed, on};
|
||||
use Livewire\WithPagination;
|
||||
use function Livewire\Volt\{layout, title, state, computed, on, updated, uses};
|
||||
|
||||
uses([WithPagination::class]);
|
||||
|
||||
layout('components.layouts.app');
|
||||
title('Medienbibliothek');
|
||||
|
|
@ -40,6 +43,12 @@ $media = computed(
|
|||
->paginate(48),
|
||||
);
|
||||
|
||||
updated([
|
||||
'search' => fn () => $this->resetPage(),
|
||||
'filterType' => fn () => $this->resetPage(),
|
||||
'filterCollection' => fn () => $this->resetPage(),
|
||||
]);
|
||||
|
||||
$collections = computed(fn () => CmsMedia::query()->whereNotNull('collection')->where('collection', '!=', '')->distinct()->pluck('collection')->sort()->values()->toArray());
|
||||
|
||||
$profiles = computed(fn () => config('flux-cms.media.profiles', []));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
|
||||
use function Livewire\Volt\{state, mount, computed};
|
||||
use Livewire\WithPagination;
|
||||
use function Livewire\Volt\{state, mount, computed, updated, uses};
|
||||
|
||||
uses([WithPagination::class]);
|
||||
|
||||
state([
|
||||
'hubId' => null,
|
||||
|
|
@ -23,6 +26,8 @@ state([
|
|||
'csvFile' => null,
|
||||
]);
|
||||
|
||||
updated(['zipSearch' => fn () => $this->resetPage()]);
|
||||
|
||||
mount(function ($hubId = null) {
|
||||
if ($hubId) {
|
||||
$hub = \App\Models\Hub::findOrFail($hubId);
|
||||
|
|
|
|||
37
tests/Feature/PortalPreviewFramingTest.php
Normal file
37
tests/Feature/PortalPreviewFramingTest.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use FluxCms\Core\Models\CmsMedia;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
/**
|
||||
* Hotfixes 09.10.2026: Website im Vorschau-iframe des Portals, Seitennavigation der Medienbibliothek.
|
||||
*/
|
||||
it('lets the portal frame the website but no other site', function () {
|
||||
config()->set('domains.domain_portal_url', 'https://portal.b2in.eu');
|
||||
|
||||
$this->get('/?cms-preview=0')
|
||||
->assertSuccessful()
|
||||
->assertHeader('Content-Security-Policy', "frame-ancestors 'self' https://portal.b2in.eu")
|
||||
->assertHeaderMissing('X-Frame-Options');
|
||||
});
|
||||
|
||||
it('keeps the media library pagination on the page after a livewire request', function () {
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
foreach (range(1, 50) as $i) {
|
||||
CmsMedia::create([
|
||||
'filename' => "bild-{$i}.jpg", 'disk' => 'public', 'path' => "cms/media/originals/{$i}.jpg",
|
||||
'type' => 'image', 'mime_type' => 'image/jpeg', 'file_size' => 1, 'conversions' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$component = Volt::test('admin.cms.media-index')
|
||||
->call('startEdit', CmsMedia::query()->first()->id)
|
||||
->call('gotoPage', 2)
|
||||
->assertSet('paginators.page', 2);
|
||||
|
||||
expect($component->html())->not->toContain('livewire/update?page=');
|
||||
|
||||
$component->set('search', 'bild-1')->assertSet('paginators.page', 1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue