# Phase 7 — Press-Release-Form-Refactor > Großes Modul-Refactor: das zentrale „Neue Pressemitteilung"-Form > wird auf das Mockup `User Neue Mitteilung presseportale.html` gehoben. > Bekommt deshalb eine eigene Phase außerhalb der bisherigen > `hub-flux`-Roadmap (Phase 0–6 sind dort abgeschlossen). **Status**: ✅ abgeschlossen · **Aufwand**: 2–3 Tage · **Risiko**: mittel (Datenmodell-Erweiterung, Editor-Format-Migration, Composer-Dependency) --- ## Ausgangslage | Datei | Status | |---|---| | `resources/views/livewire/customer/press-releases/create.blade.php` | nur 1-Spalter, ``, fehlende Felder | | `resources/views/livewire/customer/press-releases/edit.blade.php` | gleicher Stand, plus Image-Manager | | `resources/views/livewire/admin/press-releases/create.blade.php` | Admin-Variante, dünner | | `resources/views/livewire/admin/press-releases/edit.blade.php` | Admin-Variante | Das Mockup verlangt einen 2-Spalter mit eigener Settings-Sidebar (Status & Submit, Portal, Pressekontakt, Tags, Veröffentlichung, SEO). Linke Spalte: Firma-Selector, Titel, Untertitel, Editor, Medien, Anhänge, Boilerplate. --- ## Entscheidungen (vom User abgesegnet) | Frage | Entscheidung | |---|---| | Scope | **full** — Anhänge-Tabelle + Schema-Vorbereitung für Scheduling/Embargo | | Portal-Auswahl | **read-only** — Portal kommt immer aus der Firma; UI zeigt nur Badge | | HTML-Sanitizer | **`mews/purifier`** — explizit approved, wird in 7B installiert | | Pressekontakt | **genau 1 pro PM**, Single-Select aus Firmen-Kontakten; Datenmodell bleibt n:m-Pivot, Validation erzwingt `count == 1` | | Admin-Forms | **mitziehen** in 7C+7D, gleiches Layout + Admin-only Felder | | Default-Kontakt | **erster Firmen-Kontakt alphabetisch** (keine Schema-Änderung) | --- ## Päckchen-Aufteilung ### 7A — Migrations + Models **Scope:** - `press_releases.subtitle` (string 255, nullable) - `press_releases.boilerplate_override` (text, nullable) — pro PM überschreibbare Firmen-Boilerplate - `press_releases.scheduled_at` (timestamp, nullable) — Schema da, UI „bald" - `press_releases.embargo_at` (timestamp, nullable) — Schema da, UI „bald" - `companies.boilerplate` (text, nullable) — Firmenprofil-Boilerplate - Neue Tabelle `press_release_attachments` analog `press_release_images` (`disk`, `path`, `original_name`, `mime`, `size`, `sort_order`, `title`, `description`, `legacy_portal`, `legacy_id`, soft-deletes, timestamps). - Models: `PressRelease`, `Company`, neues `PressReleaseAttachment` + Factory + Relationen + Casts. - Bestehende Tests müssen grün bleiben (alle Felder nullable, keine Verhaltensänderung). **Akzeptanz** - [ ] Migration up/down sauber - [ ] `php artisan test --compact` grün (Baseline) - [ ] `php artisan db:show press_releases / companies / press_release_attachments` zeigt neue Spalten ### 7B — Editor-Integration **Scope:** - `composer require mews/purifier` (explizite Approval einholen) - Service `App\Services\PressRelease\PressReleaseHtmlSanitizer` (Allowlist: `p,br,h2,h3,strong,em,u,ul,ol,li,blockquote,a[href|rel|target]`) - Create/Edit-Form: `` → `` mit reduzierter Toolbar: ``` toolbar="heading | bold italic | bullet ordered blockquote | link | undo redo" ``` - Save-Pfad: `$this->text = $sanitizer->clean($this->text)` - Display-Pfad (`show.blade.php`, Portal-Detail-Seiten, `PressReleaseResource`): - Wenn `text` HTML-Tags enthält → `{!! $clean !!}` (sanitized) - Wenn nicht (legacy) → `{!! nl2br(e($text)) !!}` - Helper `PressRelease::renderedText(): HtmlString` - API: `PressReleaseResource` liefert weiterhin String (HTML), Doku-Update `_docs/api/v1.yml` und `dev/migration 2026/07-API-MIGRATION.md`. **Akzeptanz** - [ ] `mews/purifier` in `composer.json` - [ ] Alle bestehenden Plain-Text-PMs werden korrekt angezeigt - [ ] Neue HTML-PMs werden korrekt sanitized gespeichert - [ ] Pest-Test: `