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:
parent
9f44546082
commit
da167b713b
24 changed files with 1202 additions and 12 deletions
|
|
@ -3,6 +3,9 @@
|
|||
use App\Models\PressRelease;
|
||||
use App\Services\PressRelease\BlacklistViolationException;
|
||||
use App\Services\PressRelease\PressReleaseCoverImage;
|
||||
use App\Enums\AiImageDeclaration;
|
||||
use App\Services\PressRelease\ImageRightsConfirmationRequiredException;
|
||||
use App\Services\PressRelease\Images\ImageCorrectionService;
|
||||
use App\Services\PressRelease\PressReleaseService;
|
||||
use Flux\Flux;
|
||||
use Livewire\Attributes\Layout;
|
||||
|
|
@ -30,6 +33,11 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
|
||||
try {
|
||||
app(PressReleaseService::class)->publish($pr);
|
||||
} catch (ImageRightsConfirmationRequiredException $e) {
|
||||
Flux::toast(heading: __('Titelbild nicht bestätigt'), text: $e->getMessage(), variant: 'danger', duration: 8000);
|
||||
Flux::modal('confirm-show-publish')->close();
|
||||
|
||||
return;
|
||||
} catch (BlacklistViolationException $e) {
|
||||
Flux::toast(
|
||||
heading: __('Automatisch abgelehnt'),
|
||||
|
|
@ -69,6 +77,77 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
Flux::modal('confirm-show-archive')->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Korrekturweg (Bildkennzeichnung E.6): Bildnachweis oder
|
||||
* Portalkennzeichnung mit Pflichtgrund ändern, Bild vorläufig zurückhalten.
|
||||
*/
|
||||
public string $correctionCredit = '';
|
||||
|
||||
public string $correctionAiDeclaration = '';
|
||||
|
||||
public string $correctionReason = '';
|
||||
|
||||
public function correctImage(ImageCorrectionService $corrections): void
|
||||
{
|
||||
$pr = $this->findAdminPR($this->id);
|
||||
$image = $pr->images()->orderByDesc('is_preview')->orderBy('sort_order')->first();
|
||||
|
||||
if ($image === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'correctionReason' => ['required', 'string', 'min:5', 'max:1000'],
|
||||
'correctionCredit' => ['nullable', 'string', 'max:255'],
|
||||
'correctionAiDeclaration' => ['nullable', \Illuminate\Validation\Rule::enum(AiImageDeclaration::class)],
|
||||
], [
|
||||
'correctionReason.required' => __('Bitte geben Sie den Grund der Korrektur an.'),
|
||||
]);
|
||||
|
||||
$admin = auth()->user();
|
||||
$changed = 0;
|
||||
|
||||
if ($this->correctionCredit !== '' && $this->correctionCredit !== (string) $image->copyright) {
|
||||
$corrections->correctCredit($image, $admin, trim($this->correctionCredit), $this->correctionReason);
|
||||
$changed++;
|
||||
}
|
||||
|
||||
if ($this->correctionAiDeclaration !== '' && $this->correctionAiDeclaration !== $image->ai_declaration?->value) {
|
||||
$corrections->correctAiDeclaration($image, $admin, AiImageDeclaration::from($this->correctionAiDeclaration), $this->correctionReason);
|
||||
$changed++;
|
||||
}
|
||||
|
||||
$this->reset(['correctionCredit', 'correctionAiDeclaration', 'correctionReason']);
|
||||
|
||||
Flux::toast(
|
||||
text: $changed > 0 ? __('Korrektur protokolliert und übernommen.') : __('Keine Änderung: Die Angaben entsprechen dem aktuellen Stand.'),
|
||||
variant: $changed > 0 ? 'success' : 'warning',
|
||||
);
|
||||
}
|
||||
|
||||
public function toggleWithheld(ImageCorrectionService $corrections): void
|
||||
{
|
||||
$pr = $this->findAdminPR($this->id);
|
||||
$image = $pr->images()->orderByDesc('is_preview')->orderBy('sort_order')->first();
|
||||
|
||||
if ($image === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate(
|
||||
['correctionReason' => ['required', 'string', 'min:5', 'max:1000']],
|
||||
['correctionReason.required' => __('Bitte geben Sie den Grund an.')],
|
||||
);
|
||||
|
||||
$image->isWithheld()
|
||||
? $corrections->release($image, auth()->user(), $this->correctionReason)
|
||||
: $corrections->withhold($image, auth()->user(), $this->correctionReason);
|
||||
|
||||
$this->reset(['correctionReason']);
|
||||
|
||||
Flux::toast(text: $image->fresh()->isWithheld() ? __('Bild vorläufig zurückgehalten.') : __('Bild wieder freigegeben.'), variant: 'success');
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
$pr = PressRelease::withoutGlobalScopes()
|
||||
|
|
@ -238,6 +317,46 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
@endif
|
||||
</article>
|
||||
|
||||
{{-- ============== KORREKTURWEG BILD (E.6) ============== --}}
|
||||
@if (! $coverIsPlaceholder && isset($adminTitleImage) && $adminTitleImage && ! $adminTitleImage->isLegacyRules())
|
||||
<article class="panel" data-image-correction>
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Bildnachweis und Kennzeichnung korrigieren') }}</span>
|
||||
@if ($adminTitleImage->isWithheld())
|
||||
<span class="badge">{{ __('vorläufig zurückgehalten') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="p-5 space-y-3 max-w-[760px]">
|
||||
<p class="m-0 text-[12.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Kundenangabe und Bestätigung bleiben unverändert; jede Korrektur wird mit Grund und Bearbeiter protokolliert. Kundenangabe: :user · Portalkennzeichnung: :portal (:source).', [
|
||||
'user' => $adminTitleImage->ai_declaration_user?->label() ?? '–',
|
||||
'portal' => $adminTitleImage->ai_declaration?->label() ?? '–',
|
||||
'source' => $adminTitleImage->ai_declaration_source?->publicLabel() ?? '–',
|
||||
]) }}
|
||||
</p>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<flux:input wire:model="correctionCredit" :label="__('Neuer Bildnachweis')" :placeholder="$adminTitleImage->copyright" />
|
||||
<flux:select wire:model="correctionAiDeclaration" :label="__('Neue Portalkennzeichnung')">
|
||||
<flux:select.option value="">{{ __('unverändert') }}</flux:select.option>
|
||||
@foreach (AiImageDeclaration::cases() as $aiOption)
|
||||
<flux:select.option :value="$aiOption->value">{{ $aiOption->label() }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
</div>
|
||||
<flux:textarea wire:model="correctionReason" :label="__('Grund (Pflicht)')" rows="2" />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<flux:button variant="primary" wire:click="correctImage">{{ __('Korrektur übernehmen') }}</flux:button>
|
||||
<flux:button variant="filled" wire:click="toggleWithheld">
|
||||
{{ $adminTitleImage->isWithheld() ? __('Bild wieder freigeben') : __('Bild vorläufig zurückhalten') }}
|
||||
</flux:button>
|
||||
<flux:button variant="ghost" size="sm" href="{{ route('admin.press-releases.images.original', ['id' => $pr->id, 'image' => $adminTitleImage->id, 'purpose' => 'Sichtprüfung im Admin']) }}" target="_blank">
|
||||
{{ __('Beweisoriginal öffnen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
{{-- ============== REJECTION-HINWEIS ============== --}}
|
||||
@if ($pr->status === \App\Enums\PressReleaseStatus::Rejected && $latestRejection)
|
||||
<article class="panel" style="border-color:var(--color-err); border-left-width:3px;">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue