From 9b55e111595a8bb56e2f1bde3ac9c2b7de9dc92b Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Tue, 7 Jul 2026 15:00:40 +0000 Subject: [PATCH] =?UTF-8?q?PM-Detailseite:=20Direktlinks=20zur=20=C3=B6ffe?= =?UTF-8?q?ntlichen=20Seite=20je=20Portal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Veröffentlichte PMs zeigten im Backend nur den Status-Badge, ohne Weg zur öffentlichen Seite. Im Seitenkopf stehen jetzt Direktlinks — bei Portal::Both einer je Portal (QA-Befund 07.07.). Die URL-Logik ist als PressRelease::publicPortalLinks() zentralisiert (vorher doppelt in der Published-Mail und fehlerhaft im Nachweis-PDF: dort fehlte das /de/press-release/-Segment, die dokumentierte URL lief ins Leere; bei Both nennt der Nachweis jetzt beide URLs). Edition folgt der PM-Sprache statt hart „de". Co-Authored-By: Claude Fable 5 --- app/Mail/PressReleasePublished.php | 10 +++++ app/Models/PressRelease.php | 39 +++++++++++++++++ .../PressRelease/ProofPdfRenderer.php | 20 ++++----- .../customer/press-releases/show.blade.php | 15 ++++++- tests/Feature/PressReleaseShowPhase8aTest.php | 43 +++++++++++++++++++ tests/Feature/ProofPdfAndExtraPmTest.php | 14 ++++++ 6 files changed, 128 insertions(+), 13 deletions(-) diff --git a/app/Mail/PressReleasePublished.php b/app/Mail/PressReleasePublished.php index d11bc51e..39cb935f 100644 --- a/app/Mail/PressReleasePublished.php +++ b/app/Mail/PressReleasePublished.php @@ -16,6 +16,14 @@ class PressReleasePublished extends Mailable public string $showUrl; + /** + * Öffentliche Detailseiten je Portal, auf dem die PM erschienen ist + * (bei Portal::Both beide Spokes). + * + * @var list + */ + public array $portalLinks; + public function __construct( public readonly User $user, public readonly PressRelease $pressRelease, @@ -23,6 +31,8 @@ class PressReleasePublished extends Mailable $this->showUrl = $user->canAccessAdmin() ? route('admin.press-releases.show', $pressRelease->id) : route('me.press-releases.show', $pressRelease->id); + + $this->portalLinks = $pressRelease->publicPortalLinks(); } public function envelope(): Envelope diff --git a/app/Models/PressRelease.php b/app/Models/PressRelease.php index 7d5fe8c2..109059b0 100644 --- a/app/Models/PressRelease.php +++ b/app/Models/PressRelease.php @@ -7,6 +7,7 @@ use App\Enums\PressReleaseClassification; use App\Enums\PressReleaseContentTier; use App\Enums\PressReleasePlaceholder; use App\Enums\PressReleaseStatus; +use App\Http\Middleware\SetEdition; use App\Models\Concerns\HasUniqueSlug; use App\Scopes\PortalScope; use App\Services\PressRelease\PressReleaseHtmlSanitizer; @@ -165,6 +166,44 @@ class PressRelease extends Model return $this->hasMany(PressReleaseStatusLog::class)->orderByDesc('created_at'); } + /** + * Öffentliche Detailseiten je Portal, auf dem die PM erscheint + * (bei Portal::Both beide Spokes). Zentrale Quelle für Mail, + * Nachweis-PDF und Detailansicht. + * + * @return list + */ + public function publicPortalLinks(): array + { + if (! $this->slug) { + return []; + } + + $portals = match ($this->portal) { + Portal::Both => [Portal::Presseecho, Portal::Businessportal24], + null => [], + default => [$this->portal], + }; + + $edition = in_array($this->language, SetEdition::EDITIONS, true) + ? $this->language + : 'de'; + + return array_map(fn (Portal $portal): array => [ + 'portal' => $portal, + 'label' => $portal->label(), + 'url' => sprintf( + '%s/%s/press-release/%s', + rtrim((string) match ($portal) { + Portal::Presseecho => config('domains.domain_presseecho_url'), + default => config('domains.domain_businessportal_url'), + }, '/'), + $edition, + $this->slug, + ), + ], $portals); + } + public function kiAudits(): HasMany { return $this->hasMany(KiAudit::class)->orderByDesc('created_at'); diff --git a/app/Services/PressRelease/ProofPdfRenderer.php b/app/Services/PressRelease/ProofPdfRenderer.php index e6cc7223..d8fae064 100644 --- a/app/Services/PressRelease/ProofPdfRenderer.php +++ b/app/Services/PressRelease/ProofPdfRenderer.php @@ -90,25 +90,21 @@ class ProofPdfRenderer */ private function metaRows(PressRelease $pressRelease, string $portalLabel, string $dateLabel): array { + // Bei Portal::Both dokumentiert der Nachweis beide öffentlichen URLs. + $links = $pressRelease->publicPortalLinks(); + $urlRows = array_map(static fn (array $link): array => [ + count($links) > 1 ? 'URL '.$link['label'] : 'URL', + $link['url'], + ], $links); + return array_values(array_filter([ ['Portal', $portalLabel], ['Veröffentlicht', $dateLabel], - ['URL', $this->publicUrl($pressRelease)], + ...$urlRows, $pressRelease->category?->name ? ['Kategorie', (string) $pressRelease->category->name] : null, ])); } - private function publicUrl(PressRelease $pressRelease): string - { - $base = match ($pressRelease->portal) { - Portal::Presseecho => config('domains.domain_presseecho_url'), - Portal::Businessportal24 => config('domains.domain_businessportal_url'), - default => config('app.url'), - }; - - return rtrim((string) $base, '/').'/'.ltrim((string) $pressRelease->slug, '/'); - } - private function previewText(PressRelease $pressRelease): string { $plain = trim(html_entity_decode(strip_tags((string) $pressRelease->text))); diff --git a/resources/views/livewire/customer/press-releases/show.blade.php b/resources/views/livewire/customer/press-releases/show.blade.php index 824eab9b..0c020e4b 100644 --- a/resources/views/livewire/customer/press-releases/show.blade.php +++ b/resources/views/livewire/customer/press-releases/show.blade.php @@ -209,6 +209,9 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends return [ 'pr' => $pr, + 'publicLinks' => $pr->status === PressReleaseStatus::Published + ? $pr->publicPortalLinks() + : [], 'creditBalance' => $user->creditBalance(), 'canBoost' => $boostService->canBoost($pr), 'isBoosted' => $boostService->isBoosted($pr), @@ -353,7 +356,17 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends

-
+
+ {{-- Direktlinks zur öffentlichen Seite (QA-Befund 07.07.): + bei Portal::Both je Portal ein eigener Button. --}} + @foreach ($publicLinks as $link) + + {{ count($publicLinks) > 1 + ? __('Auf :portal ansehen', ['portal' => $link['label']]) + : __('Öffentlich ansehen') }} + + @endforeach @if ($canEdit) {{ __('Bearbeiten') }} diff --git a/tests/Feature/PressReleaseShowPhase8aTest.php b/tests/Feature/PressReleaseShowPhase8aTest.php index c0136457..c33df0b9 100644 --- a/tests/Feature/PressReleaseShowPhase8aTest.php +++ b/tests/Feature/PressReleaseShowPhase8aTest.php @@ -1,5 +1,6 @@ assertDontSee('Eigener Abbinder (Boilerplate)'); }); +test('customer show links to the public page of a published release', function () { + /** @var TestCase $this */ + ['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a([ + 'status' => PressReleaseStatus::Published, + 'published_at' => now()->subHour(), + 'slug' => 'oeffentliche-pm', + 'language' => 'de', + ]); + $this->actingAs($customer); + + LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) + ->assertSee('Öffentlich ansehen') + ->assertSee(config('domains.domain_presseecho_url').'/de/press-release/oeffentliche-pm', escape: false); +}); + +test('customer show links to both portals when the release is published on both', function () { + /** @var TestCase $this */ + ['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a([ + 'status' => PressReleaseStatus::Published, + 'published_at' => now()->subHour(), + 'portal' => Portal::Both, + 'slug' => 'beide-portale-pm', + 'language' => 'de', + ]); + $this->actingAs($customer); + + LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) + ->assertSee('Auf Presseecho ansehen') + ->assertSee('Auf Businessportal24 ansehen') + ->assertSee(config('domains.domain_presseecho_url').'/de/press-release/beide-portale-pm', escape: false) + ->assertSee(config('domains.domain_businessportal_url').'/de/press-release/beide-portale-pm', escape: false); +}); + +test('customer show shows no public link for drafts', function () { + /** @var TestCase $this */ + ['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a(); + $this->actingAs($customer); + + LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) + ->assertDontSee('Öffentlich ansehen'); +}); + test('admin show zeigt den Untertitel direkt unter dem Titel', function () { /** @var TestCase $this */ $admin = makeAdminForShowPhase8a(); diff --git a/tests/Feature/ProofPdfAndExtraPmTest.php b/tests/Feature/ProofPdfAndExtraPmTest.php index 80b2bd57..5eb995d0 100644 --- a/tests/Feature/ProofPdfAndExtraPmTest.php +++ b/tests/Feature/ProofPdfAndExtraPmTest.php @@ -1,5 +1,6 @@ toBeGreaterThan(800); }); +test('the rendered proof documents the real public url of the release', function () { + $pr = PressRelease::factory()->published()->create([ + 'portal' => Portal::Presseecho, + 'slug' => 'nachweis-pm', + 'language' => 'de', + ]); + + $pdf = app(ProofPdfRenderer::class)->render($pr); + + // Vorher fehlte das /de/press-release/-Segment — die URL im Nachweis lief ins Leere. + expect($pdf)->toContain(config('domains.domain_presseecho_url').'/de/press-release/nachweis-pm'); +}); + test('the proof download requires a prior purchase', function () { $user = customerWithRole(); $pr = PressRelease::factory()->published()->create(['user_id' => $user->id]);