images: Lebenszyklus — Veröffentlichungsgate, published_ever_at, Snapshot der veröffentlichten Fassung, Sofortlöschung, Legacy-Bereinigung, Beweiszugriff, Korrekturweg (Bildkennzeichnung Block E)

- publish()/changeStatusFromAdmin(): kein Bild aus Formular v2 ohne gültige Bestätigung (ImageRightsConfirmationRequiredException, Admin-Seite meldet es); published_ever_at an PM und Bildern; PublishedImageSnapshot (Cover + JSON mit Nachweis/Kennzeichnung)
- deleteFromAdmin() und press-releases:purge-drafts löschen Bilder nie veröffentlichter Meldungen samt Original und Bestätigung sofort (E4, O1)
- press-releases:backfill-published-ever (Status-Log), press-release-images:purge-inactive-legacy (E-B11, Trockenlauf Standard)
- Admin-Route für Beweisoriginal und veröffentlichte Fassung nur mit Zweck, ActivityType EvidenceAccess; Architekturtest: original_path nur in vier Dateien
- ImageCorrectionService + image_declaration_corrections (insert-only), withheld_at blendet das Bild öffentlich aus (publiclyVisible-Scope), Korrekturpanel auf der Admin-Seite
- Systemstatus: fehlgeschlagene Herkunftsprüfungen der letzten 7 Tage

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-22 09:53:49 +00:00
parent 9f44546082
commit da167b713b
24 changed files with 1202 additions and 12 deletions

View file

@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* Insert-only-Protokoll des Korrekturwegs (Bildkennzeichnung E.6): welcher
* Admin wann welches Feld eines Bildes mit welchem Grund geändert hat.
*/
class ImageDeclarationCorrection extends Model
{
public const UPDATED_AT = null;
public const FIELD_CREDIT = 'copyright';
public const FIELD_AI_DECLARATION = 'ai_declaration';
public const FIELD_WITHHELD = 'withheld';
protected $fillable = [
'press_release_image_id',
'press_release_id',
'admin_id',
'field',
'old_value',
'new_value',
'old_source',
'reason',
'created_at',
];
protected function casts(): array
{
return [
'created_at' => 'datetime',
];
}
protected static function booted(): void
{
static::updating(fn () => throw new \LogicException('image_declaration_corrections ist insert-only.'));
static::deleting(fn () => throw new \LogicException('image_declaration_corrections wird nicht gelöscht.'));
}
/**
* @return BelongsTo<PressReleaseImage, $this>
*/
public function image(): BelongsTo
{
return $this->belongsTo(PressReleaseImage::class, 'press_release_image_id');
}
/**
* @return BelongsTo<User, $this>
*/
public function admin(): BelongsTo
{
return $this->belongsTo(User::class, 'admin_id');
}
}