diff --git a/app/Mail/PressReleaseHeldForReview.php b/app/Mail/PressReleaseHeldForReview.php index 385fa538..27e2cf8d 100644 --- a/app/Mail/PressReleaseHeldForReview.php +++ b/app/Mail/PressReleaseHeldForReview.php @@ -7,6 +7,7 @@ use App\Enums\EmailNotificationType; use App\Mail\Concerns\Unsubscribable; use App\Models\PressRelease; use App\Models\User; +use App\Services\Auth\MailAccessLink; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; @@ -35,6 +36,16 @@ class PressReleaseHeldForReview extends Mailable public int $maxAttempts; + /** + * Empfänger ohne Konto (API-Meldung, Roadmap 6.17): Adresse dieser Mail + * und Link, der einen Anmeldelink an sie schickt. Der Link zum Kundenbereich + * führte sonst auf eine Anmeldeseite für ein Konto, das der Empfänger + * nicht kennt. + */ + public ?string $accessEmail = null; + + public ?string $accessUrl = null; + public function __construct( public readonly User $user, public readonly PressRelease $pressRelease, @@ -55,6 +66,8 @@ class PressReleaseHeldForReview extends Mailable ]); if ($recipientEmail !== null && mb_strtolower(trim($recipientEmail)) !== mb_strtolower((string) $user->email)) { + $this->accessEmail = trim($recipientEmail); + $this->accessUrl = app(MailAccessLink::class)->url($this->accessEmail, $pressRelease, ConversionSource::MailReview); $this->configureAddressUnsubscribe($recipientEmail, EmailNotificationType::PressReleaseRejected); } else { $this->configureUnsubscribe($user, EmailNotificationType::PressReleaseRejected); diff --git a/app/Services/Auth/MailAccessLink.php b/app/Services/Auth/MailAccessLink.php index b4d816d4..b2eb012a 100644 --- a/app/Services/Auth/MailAccessLink.php +++ b/app/Services/Auth/MailAccessLink.php @@ -11,7 +11,7 @@ use Illuminate\Support\Facades\URL; /** * „Anmeldelink an diese Adresse“ aus einer Kundenmail (Roadmap 6.17). * - * Veröffentlichungs- und Ablehnungsmails zu API-Meldungen gehen an den + * Veröffentlichungs-, Ablehnungs- und Prüfmails zu API-Meldungen gehen an den * Pressekontakt oder die Firmen-E-Mail — Empfänger, die aus ihrer Sicht kein * Konto haben. Der Link führt auf eine Seite, die mit einem Klick einen * Anmeldelink an genau die Adresse schickt, an die die Mail ging, und nach diff --git a/resources/views/emails/press-release/held-for-review.blade.php b/resources/views/emails/press-release/held-for-review.blade.php index 564e6c43..e2463c8e 100644 --- a/resources/views/emails/press-release/held-for-review.blade.php +++ b/resources/views/emails/press-release/held-for-review.blade.php @@ -24,7 +24,21 @@
-Bei Fragen erreichen Sie uns über den Hilfe-Bereich auf pressekonto. diff --git a/resources/views/livewire/auth/mail-access.blade.php b/resources/views/livewire/auth/mail-access.blade.php index e22fc878..bf566bf7 100644 --- a/resources/views/livewire/auth/mail-access.blade.php +++ b/resources/views/livewire/auth/mail-access.blade.php @@ -17,8 +17,8 @@ use Livewire\Component; /** * Anmeldelink an die Adresse, an die eine Kundenmail ging (Roadmap 6.17). * - * Erreichbar nur über den signierten Link aus der Veröffentlichungs- oder - * Ablehnungsmail (MailAccessLink). Ein Klick schickt den Anmeldelink; nach + * Erreichbar nur über den signierten Link aus der Veröffentlichungs-, + * Ablehnungs- oder Prüfmail (MailAccessLink). Ein Klick schickt den Anmeldelink; nach * der Anmeldung landet der Empfänger auf der Meldung. Die Seite zeigt die * Adresse nur maskiert. */ @@ -98,7 +98,7 @@ new #[Layout('components.layouts.auth.pressekonto', ['heading' => 'Ohne Passwort { $source = ConversionSource::parse((string) request()->query('src')); - return in_array($source, [ConversionSource::MailPublish, ConversionSource::MailReject], true) ? $source : ConversionSource::Organic; + return in_array($source, [ConversionSource::MailPublish, ConversionSource::MailReject, ConversionSource::MailReview], true) ? $source : ConversionSource::Organic; } private function pressRelease(int $id): ?PressRelease diff --git a/tests/Feature/Auth/MailAccessTest.php b/tests/Feature/Auth/MailAccessTest.php index 9cd709ea..81f8d590 100644 --- a/tests/Feature/Auth/MailAccessTest.php +++ b/tests/Feature/Auth/MailAccessTest.php @@ -5,6 +5,7 @@ use App\Enums\ConversionSource; use App\Enums\Portal; use App\Enums\PressReleaseStatus; use App\Mail\MagicLoginLink; +use App\Mail\PressReleaseHeldForReview; use App\Mail\PressReleasePublished; use App\Mail\PressReleaseRejected; use App\Models\Company; @@ -93,6 +94,32 @@ test('the rejection mail tells recipients without an account that they can revis ->and($own)->not->toContain('Anmeldelink anfordern'); }); +test('the held-for-review mail lets recipients without an account see the status via a sign-in link', function () { + /** @var TestCase $this */ + $release = apiRelease(['status' => PressReleaseStatus::Review->value]); + $mail = new PressReleaseHeldForReview($release->user, $release, false, 'Frau Muster', 'presse@firma.de'); + $html = $mail->render(); + + expect($mail->accessUrl)->toContain('/zugang?') + ->and(accessQuery($mail->accessUrl)['src'])->toBe(ConversionSource::MailReview->value) + ->and($html)->toContain('Sie müssen nichts tun') + ->and($html)->toContain('Den Stand der Prüfung sehen?') + ->and($html)->toContain('weder ein Konto noch ein Passwort') + ->and($html)->toContain('presse@firma.de') + ->and($html)->toContain('Anmeldelink anfordern') + ->and($html)->not->toContain('Stand der Prüfung ansehen'); + + // Die Zugangsseite übernimmt die Herkunft aus der Prüfmail. + $this->get($mail->accessUrl)->assertOk(); + expect(ConversionEvent::query()->where('type', ConversionEventType::ManagePageOpened->value)->sole()->source) + ->toBe(ConversionSource::MailReview); + + // Wer selbst eingereicht hat, öffnet die Meldung wie bisher im Konto. + $own = (new PressReleaseHeldForReview($release->user, $release, false))->render(); + expect($own)->toContain('Stand der Prüfung ansehen') + ->and($own)->not->toContain('Anmeldelink anfordern'); +}); + // --------------------------------------------------------- Zugangsseite test('the access page only opens with a valid signature and shows the address masked', function () {