Decision-Update Abschnitt 12 (E15, S24): Sichtung auf offensichtliche Probleme, keine Rechteprüfung, öffentlich weiterhin „automatisierte Prüfung". - ImagePrecheckManager mit openai- und deterministic-Treiber (Muster der Text-Prüfstrecke), Bild auf 1024 px, Metadaten gestrippt, JPEG als Data-URI, Antwortschema 12.4; Fehler → Ausgang `error`, durchlassen mit Vermerk. - Auswertung 12.3: reject (Stock-Wasserzeichen, Nacktheit/Gewalt, Dokument ab 0,85), queue (Screenshot, fremdes Logo, Kennzeichen ab 0,6 sowie Ablehnungskategorien 0,6–0,85), hint (Personen, „wirkt KI" nur bei „ohne KI", technischer Mangel). Stufe 1 wertet nur das Wasserzeichen; die KI setzt nie die Kennzeichnung (E9). Herabstufung per Config (12.8). - Uploader: Check vor jedem Schreiben, Ablehnung speichert nichts und landet als ki_audits `image_precheck` an der PM; Hash-Bindung zwischen Vorprüfung im Formular und Speichern; Spalten precheck*, Snapshot `precheck_snapshot` im Bestätigungsprotokoll, Archiv-Payload erweitert. - Queue: Bild zurückgehalten (withheld_at), Meldung läuft weiter; Admin entscheidet mit Vermerk (Freigabe/Ablehnung → ki_audits `image_precheck_review`); Ansicht „Bild-Sichtung" in der PM-Liste, Sichtungsblock auf der PM-Seite, Systemstatus-Zähler. - Formular v2: Ablehnungsgrund unter dem Dateifeld, Hinweise nach der Dateiauswahl, „wird kurz gesichtet" an der Karte; API 422 mit Grund, Warnung `image_under_review`. - Config images.precheck (IMAGE_PRECHECK aus, STAGE 1); 17 Tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
96 lines
3.7 KiB
PHP
96 lines
3.7 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Resources;
|
||
|
||
use App\Models\PressReleaseImage;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Http\Resources\Json\JsonResource;
|
||
|
||
/**
|
||
* @mixin PressReleaseImage
|
||
*/
|
||
class PressReleaseImageResource extends JsonResource
|
||
{
|
||
/**
|
||
* Transform the resource into an array.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
public function toArray(Request $request): array
|
||
{
|
||
$variants = is_array($this->variants) ? $this->variants : [];
|
||
$variantUrls = [];
|
||
|
||
// Variant-URLs über das Model auflösen, damit sie – wie die Original-URL –
|
||
// je nach Disk entweder direkt (public) oder über die geschützte
|
||
// Media-Route (local) zeigen. Sonst 404 für private Medien.
|
||
foreach ($variants as $key => $relativePath) {
|
||
if (is_string($relativePath) && filled($relativePath) && filled($url = $this->variantUrl($key))) {
|
||
$variantUrls[$key] = $url;
|
||
}
|
||
}
|
||
|
||
return [
|
||
'id' => $this->id,
|
||
'press_release_id' => $this->press_release_id,
|
||
'url' => $this->url(),
|
||
'urls' => array_merge(['original' => $this->url()], $variantUrls),
|
||
'variants' => $variants,
|
||
'disk' => $this->disk,
|
||
'path' => $this->path,
|
||
'title' => $this->title,
|
||
'description' => $this->description,
|
||
'copyright' => $this->copyright,
|
||
// Bildkennzeichnung Block C: Portalkennzeichnung, Kundenangabe
|
||
// und Herkunft getrennt (Decision-Update S6); Bestätigung mit
|
||
// Kennung. `urls.original` bleibt das Cover — das Beweisoriginal
|
||
// ist nie über die API erreichbar (E.4).
|
||
'ai_declaration' => $this->ai_declaration?->value,
|
||
'ai_declaration_user' => $this->ai_declaration_user?->value,
|
||
'ai_declaration_source' => $this->ai_declaration_source?->value,
|
||
'ai_declaration_locked' => (bool) $this->ai_declaration_locked,
|
||
'provenance_status' => $this->provenance_status?->value,
|
||
'license_url' => $this->license_url,
|
||
'source_url' => $this->source_url,
|
||
'rights_confirmation' => $this->whenLoaded('latestRightsConfirmation', fn () => $this->latestRightsConfirmation === null ? null : [
|
||
'version' => $this->latestRightsConfirmation->legal_text_key,
|
||
'agb_version' => $this->latestRightsConfirmation->agb_version,
|
||
'confirmed_at' => $this->latestRightsConfirmation->confirmed_at->toIso8601String(),
|
||
'channel' => $this->latestRightsConfirmation->channel->value,
|
||
]),
|
||
'warnings' => $this->warnings(),
|
||
'is_preview' => $this->is_preview,
|
||
'sort_order' => $this->sort_order,
|
||
'width' => $this->width,
|
||
'height' => $this->height,
|
||
'mime' => $this->mime,
|
||
'created_at' => $this->created_at?->toIso8601String(),
|
||
'updated_at' => $this->updated_at?->toIso8601String(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Maschinenlesbare Hinweise (Feedback F04): Prüfung fehlgeschlagen,
|
||
* Kennzeichnung durch geprüfte Herkunftsdaten angehoben (Phase 2).
|
||
*
|
||
* @return list<string>
|
||
*/
|
||
private function warnings(): array
|
||
{
|
||
$warnings = [];
|
||
|
||
if ($this->provenance_status?->value === 'error') {
|
||
$warnings[] = 'provenance_check_failed';
|
||
}
|
||
|
||
if ((bool) $this->ai_declaration_locked && $this->ai_declaration !== $this->ai_declaration_user) {
|
||
$warnings[] = 'ai_declaration_raised_by_metadata';
|
||
}
|
||
|
||
if ($this->awaitsPrecheckReview()) {
|
||
$warnings[] = 'image_under_review';
|
||
}
|
||
|
||
return $warnings;
|
||
}
|
||
}
|