mail: adressbezogene Abmeldung fuer Empfaenger ohne Konto (Roadmap 1d.3)
Der Abmeldelink in API-Benachrichtigungen zeigte auf das Konto des Einlieferers - ein Klick des Pressekontakts haette connektar abgemeldet. Jetzt traegt der Link die Empfaengeradresse (signiert), der Zustand liegt als Unsubscribed in der Sperrliste. Anmeldelinks, die die Person selbst anfordert, gehen weiter durch; harte Bounces sperren alles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
13716b9c06
commit
9380937562
15 changed files with 362 additions and 20 deletions
|
|
@ -35,7 +35,13 @@ class EmailSuppressionService
|
|||
return $email === '' ? null : $email;
|
||||
}
|
||||
|
||||
public function isSuppressed(?string $email): bool
|
||||
/**
|
||||
* @param bool $includeUnsubscribed false = nur Zustellprobleme zählen;
|
||||
* eine Abmeldung (1d.3) hält
|
||||
* Benachrichtigungen ab, aber nicht,
|
||||
* was die Person selbst anfordert
|
||||
*/
|
||||
public function isSuppressed(?string $email, bool $includeUnsubscribed = true): bool
|
||||
{
|
||||
$key = self::normalise($email);
|
||||
|
||||
|
|
@ -43,7 +49,11 @@ class EmailSuppressionService
|
|||
return false;
|
||||
}
|
||||
|
||||
return EmailSuppression::query()->active()->where('email', $key)->exists();
|
||||
return EmailSuppression::query()
|
||||
->active()
|
||||
->where('email', $key)
|
||||
->when(! $includeUnsubscribed, fn ($q) => $q->where('reason', '!=', EmailSuppressionReason::Unsubscribed->value))
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -53,7 +63,7 @@ class EmailSuppressionService
|
|||
* @param list<string> $emails
|
||||
* @return list<string> normalisiert
|
||||
*/
|
||||
public function suppressedAmong(array $emails): array
|
||||
public function suppressedAmong(array $emails, bool $includeUnsubscribed = true): array
|
||||
{
|
||||
$keys = array_values(array_filter(array_map(self::normalise(...), $emails)));
|
||||
|
||||
|
|
@ -61,7 +71,53 @@ class EmailSuppressionService
|
|||
return [];
|
||||
}
|
||||
|
||||
return EmailSuppression::query()->active()->whereIn('email', $keys)->pluck('email')->all();
|
||||
return EmailSuppression::query()
|
||||
->active()
|
||||
->whereIn('email', $keys)
|
||||
->when(! $includeUnsubscribed, fn ($q) => $q->where('reason', '!=', EmailSuppressionReason::Unsubscribed->value))
|
||||
->pluck('email')
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abmeldung durch den Empfänger (Roadmap 1d.3): für Adressen ohne Konto
|
||||
* — Pressekontakt, Firmen-E-Mail — ist die Sperrliste der einzige Ort,
|
||||
* an dem „will keine Benachrichtigungen" stehen kann. Eine bestehende
|
||||
* Zustellsperre bleibt, was sie ist.
|
||||
*/
|
||||
public function unsubscribe(string $email, string $method = 'link'): ?EmailSuppression
|
||||
{
|
||||
$key = self::normalise($email);
|
||||
|
||||
if ($key === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$existing = EmailSuppression::query()->active()->where('email', $key)->first();
|
||||
|
||||
if ($existing !== null && $existing->reason !== EmailSuppressionReason::Unsubscribed) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
return $this->suppress($key, EmailSuppressionReason::Unsubscribed, 'unsubscribe', 'Abmeldung durch Empfänger ('.$method.')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Abmeldung zurücknehmen — nur eine Abmeldung; Zustellsperren löst nur
|
||||
* der Admin.
|
||||
*/
|
||||
public function resubscribe(string $email): bool
|
||||
{
|
||||
$key = self::normalise($email);
|
||||
$suppression = $key === null ? null : EmailSuppression::query()->active()->where('email', $key)->where('reason', EmailSuppressionReason::Unsubscribed->value)->first();
|
||||
|
||||
if ($suppression === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->release($suppression, null, 'Abmeldung durch Empfänger zurückgenommen');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue