Die Mail „Ihre Pressemitteilung wird persönlich geprüft“ verlinkte auch bei API-Meldungen in den Kundenbereich. Empfänger ohne Konto landeten auf der Passwort-Anmeldung. Wie in der Veröffentlichungs- und Ablehnungsmail schickt jetzt ein Klick einen Anmeldelink an die Adresse der Mail; die Zugangsseite übernimmt die Herkunft mail_review für den Funnel. Changelog: Verbessert Public: Auch die Mail zur persönlichen Prüfung führt Pressekontakte ohne Passwort zu ihrer Meldung. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
142 lines
5.7 KiB
PHP
142 lines
5.7 KiB
PHP
<?php
|
|
|
|
use App\Enums\ConversionEventType;
|
|
use App\Enums\ConversionSource;
|
|
use App\Models\Company;
|
|
use App\Models\PressRelease;
|
|
use App\Scopes\PortalScope;
|
|
use App\Services\Auth\ContactAccessService;
|
|
use App\Services\Auth\MailAccessLink;
|
|
use App\Services\Company\NewsroomClaimService;
|
|
use App\Services\Conversion\ConversionRecorder;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Locked;
|
|
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-,
|
|
* 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.
|
|
*/
|
|
new #[Layout('components.layouts.auth.pressekonto', ['heading' => 'Ohne Passwort anmelden', 'eyebrow' => 'Zugang zu Ihrer Pressemitteilung', 'topRightLabel' => 'Lieber mit Passwort?', 'topRightLinkText' => 'Anmelden', 'topRightLinkHref' => '/login'])] class extends Component {
|
|
#[Locked]
|
|
public string $token = '';
|
|
|
|
#[Locked]
|
|
public string $source = '';
|
|
|
|
public string $maskedEmail = '';
|
|
|
|
public string $title = '';
|
|
|
|
public bool $sent = false;
|
|
|
|
public function mount(MailAccessLink $links, ConversionRecorder $recorder): mixed
|
|
{
|
|
$data = $links->decode(request()->query('t'));
|
|
$pressRelease = $data === null ? null : $this->pressRelease($data['press_release_id']);
|
|
|
|
abort_if($pressRelease === null, 404);
|
|
|
|
$user = auth()->user();
|
|
|
|
if ($user !== null && mb_strtolower((string) $user->email) === $data['email'] && $user->can('view', $pressRelease)) {
|
|
return redirect()->route('me.press-releases.show', ['id' => $pressRelease->id]);
|
|
}
|
|
|
|
$this->token = (string) request()->query('t');
|
|
$this->source = $this->sourceFromRequest()->value;
|
|
$this->maskedEmail = NewsroomClaimService::mask($data['email']);
|
|
$this->title = (string) $pressRelease->title;
|
|
|
|
$company = $this->company($pressRelease);
|
|
|
|
if ($company !== null) {
|
|
$recorder->recordManagePageOpened(request(), $company, ConversionSource::parse($this->source), $pressRelease);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function requestLink(MailAccessLink $links, ContactAccessService $contactAccess, ConversionRecorder $recorder): void
|
|
{
|
|
$data = $links->decode($this->token);
|
|
$pressRelease = $data === null ? null : $this->pressRelease($data['press_release_id']);
|
|
|
|
if ($pressRelease === null) {
|
|
return;
|
|
}
|
|
|
|
$key = 'mail-access|'.hash('sha256', $data['email']);
|
|
|
|
if (RateLimiter::tooManyAttempts($key, 3)) {
|
|
$this->addError('link', __('Zu viele Anfragen. Bitte versuchen Sie es in :minutes Minuten erneut.', [
|
|
'minutes' => max(1, (int) ceil(RateLimiter::availableIn($key) / 60)),
|
|
]));
|
|
|
|
return;
|
|
}
|
|
|
|
RateLimiter::hit($key, 3600);
|
|
|
|
$source = ConversionSource::parse($this->source);
|
|
$sent = $contactAccess->requestMagicAccess($data['email'], request()->ip(), $source, $pressRelease->company_id, $pressRelease->id);
|
|
$company = $this->company($pressRelease);
|
|
|
|
if ($sent && $company !== null) {
|
|
$recorder->record(ConversionEventType::AccessRequested, $source, $company, $pressRelease);
|
|
}
|
|
|
|
$this->sent = true;
|
|
}
|
|
|
|
private function sourceFromRequest(): ConversionSource
|
|
{
|
|
$source = ConversionSource::parse((string) request()->query('src'));
|
|
|
|
return in_array($source, [ConversionSource::MailPublish, ConversionSource::MailReject, ConversionSource::MailReview], true) ? $source : ConversionSource::Organic;
|
|
}
|
|
|
|
private function pressRelease(int $id): ?PressRelease
|
|
{
|
|
return PressRelease::query()->withoutGlobalScope(PortalScope::class)->find($id);
|
|
}
|
|
|
|
private function company(PressRelease $pressRelease): ?Company
|
|
{
|
|
return $pressRelease->company_id === null
|
|
? null
|
|
: Company::query()->withoutGlobalScope(PortalScope::class)->find($pressRelease->company_id);
|
|
}
|
|
}; ?>
|
|
|
|
<div>
|
|
@if ($sent)
|
|
<div class="mb-6 rounded-[2px] border border-bg-rule bg-bg-elev px-4 py-3 text-[13.5px] leading-[1.6] text-ink-2" role="status">
|
|
{{ __('Wir haben einen Anmeldelink an :email geschickt. Er ist 15 Minuten gültig; nach dem Klick landen Sie direkt bei Ihrer Pressemitteilung.', ['email' => $maskedEmail]) }}
|
|
</div>
|
|
@else
|
|
<p class="text-[13.5px] text-ink-2 leading-[1.65] !-mt-2 mb-4">
|
|
{{ __('Sie brauchen weder ein Konto noch ein Passwort. Wir schicken einen Anmeldelink an die Adresse, an die unsere Mail ging:') }}
|
|
</p>
|
|
<p class="mb-2 font-mono text-[14px] text-ink">{{ $maskedEmail }}</p>
|
|
<p class="mb-6 text-[12.5px] text-ink-3">{{ __('Pressemitteilung:') }} <span class="text-ink-2">{{ $title }}</span></p>
|
|
|
|
@error('link')
|
|
<p class="field-error mb-4">{{ $message }}</p>
|
|
@enderror
|
|
|
|
<button type="button" class="auth-btn-primary" wire:click="requestLink" wire:loading.attr="disabled" wire:target="requestLink">
|
|
<span wire:loading.remove wire:target="requestLink">{{ __('Anmeldelink senden') }}</span>
|
|
<span wire:loading wire:target="requestLink">{{ __('Wird gesendet …') }}</span>
|
|
</button>
|
|
@endif
|
|
|
|
<p class="mt-6 text-[12.5px] text-ink-3 leading-[1.6]">
|
|
{{ __('Nach der Anmeldung können Sie die Meldung ansehen und korrigieren, eine abgelehnte Meldung überarbeiten und erneut einreichen, die Aufrufe verfolgen und den Newsroom Ihrer Firma pflegen. Das ist kostenlos.') }}
|
|
</p>
|
|
</div>
|