Phase 7 (Spur D, E2 revidiert): KI-Vorprüfung VOR der Zahlung im Modal — rot lehnt ohne Zahlung ab, gelb/grün führt zum Stripe-Checkout (SinglePurchaseType::Change, 10 € netto), nach Zahlungseingang übernimmt der Webhook die geprüfte Fassung sofort (applyPaid: Snapshot, Sperre, Verlauf). Nur einmal je PM; Obergrenze legal.change_max_distance_percent (50 %) verhindert die verdeckte Selbst-Depublizierung. Phase 5 (Spur B): strukturiertes Schwärzungsformular mit Pflicht-Vorschau im verifizierten Web-Bereich UND als Modal im Kunden-Panel; Admin-Editor wird aus payload.redaction vorbefüllt. Umsetzungsmail LegalRequestExecuted an Antragsteller, Bestätigungsmail PressReleaseChangeApplied (Spur A/D, neuer abbestellbarer Typ press_release_corrected). Außerdem: - Re-Sync-Sperre + Sitemap-Invalidierung fest in depublish() verankert (Lücke im bezahlten Webhook-Pfad geschlossen) - Öffentliche „Rechtliches"-Seite als zweistufige Triage: primär der Portal-Anmeldelink, Formular nur Fallback mit Identifizierungs-Hinweis; Store auf report/dsgvo/personal_rights gehärtet - Panel-Button „Ändern/Melden" öffnet Anliegen-Modal (4 Spuren) statt des Web-Formulars - Pressekontakt-Fallback für Legacy-Bestände (30k PMs ohne Verknüpfung): Detailseite + pressContactEmail() nutzen PM-Kontakt → Firmen-Kontakt → Firmendaten - Korrektur-Gate entity-fest (Editor-Normalisierung zählt nicht als Änderung), toEditorHtml() in Admin-Edit + Korrektur-Panel, Wochenlimit-Hinweis in Text und Bremsen-Meldung Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
2.5 KiB
PHP
83 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Mail;
|
|
|
|
use App\Enums\EmailNotificationType;
|
|
use App\Enums\LegalRequestType;
|
|
use App\Mail\Concerns\Unsubscribable;
|
|
use App\Models\LegalRequest;
|
|
use App\Models\PressRelease;
|
|
use App\Models\User;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Mail\Mailable;
|
|
use Illuminate\Mail\Mailables\Content;
|
|
use Illuminate\Mail\Mailables\Envelope;
|
|
use Illuminate\Queue\SerializesModels;
|
|
|
|
/**
|
|
* Bestätigung an den Auftraggeber, dass eine Korrektur (Spur A, kostenlos)
|
|
* oder eine bezahlte Änderung (Spur D) an seiner veröffentlichten PM
|
|
* übernommen wurde und sofort öffentlich sichtbar ist.
|
|
*/
|
|
class PressReleaseChangeApplied extends Mailable
|
|
{
|
|
use Queueable, SerializesModels, Unsubscribable;
|
|
|
|
public string $showUrl;
|
|
|
|
public bool $isPaidChange;
|
|
|
|
/** @var list<string> Deutsche Labels der geänderten Felder. */
|
|
public array $changedFieldLabels;
|
|
|
|
public function __construct(
|
|
public readonly User $user,
|
|
public readonly PressRelease $pressRelease,
|
|
public readonly LegalRequest $legalRequest,
|
|
) {
|
|
$this->showUrl = $user->canAccessAdmin()
|
|
? route('admin.press-releases.show', $pressRelease->id)
|
|
: route('me.press-releases.show', $pressRelease->id);
|
|
|
|
$this->isPaidChange = $legalRequest->type === LegalRequestType::Change;
|
|
$this->changedFieldLabels = $this->resolveChangedFieldLabels($legalRequest);
|
|
|
|
$this->configureUnsubscribe($user, EmailNotificationType::PressReleaseCorrected);
|
|
}
|
|
|
|
public function envelope(): Envelope
|
|
{
|
|
$prefix = $this->isPaidChange
|
|
? 'Ihre Änderung ist live'
|
|
: 'Ihre Korrektur ist live';
|
|
|
|
return new Envelope(
|
|
subject: $prefix.': '.$this->pressRelease->title,
|
|
);
|
|
}
|
|
|
|
public function content(): Content
|
|
{
|
|
return new Content(view: 'emails.press-release.change-applied');
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
private function resolveChangedFieldLabels(LegalRequest $legalRequest): array
|
|
{
|
|
$labels = [
|
|
'title' => 'Titel',
|
|
'subtitle' => 'Untertitel',
|
|
'text' => 'Text',
|
|
'keywords' => 'Keywords',
|
|
];
|
|
|
|
return collect((array) ($legalRequest->payload['fields'] ?? []))
|
|
->filter(fn (array $meta): bool => (bool) ($meta['changed'] ?? false))
|
|
->keys()
|
|
->map(fn (string $field): string => $labels[$field] ?? $field)
|
|
->values()
|
|
->all();
|
|
}
|
|
}
|