pressReleaseId = $pressReleaseId; } public function openUploadForm(): void { $this->isUploadFormOpen = true; } /** * Beim Wechsel des Lizenztyps das Detail-Feld leeren — sonst klebt * z. B. der zuvor gewählte CC-Wert (cc_by) im Freitextfeld * „Lizenzdetails / Begründung" von „Sonstiges". Für KI-Bilder wird der * öffentliche Bildnachweis vorgeschlagen (AI-Act-Kennzeichnung). */ public function updatedNewLicenseType(): void { $this->newLicenseDetail = ''; $this->newAiTermsConfirmed = false; $this->resetErrorBag('newLicenseDetail'); $this->suggestAiCopyright(); } /** * Tool-Angabe in den Bildnachweis-Vorschlag übernehmen, solange der * Nutzer den Nachweis nicht selbst überschrieben hat. */ public function updatedNewLicenseDetail(): void { $this->suggestAiCopyright(); } private function suggestAiCopyright(): void { if ($this->newLicenseType !== ImageLicenseType::AiGenerated->value) { return; } if (filled($this->newCopyright) && ! str_starts_with($this->newCopyright, __('Bild: KI-generiert'))) { return; } $this->newCopyright = filled($this->newLicenseDetail) ? __('Bild: KI-generiert (:tool)', ['tool' => trim($this->newLicenseDetail)]) : __('Bild: KI-generiert'); } public function closeUploadForm(): void { $this->resetUploadForm(); $this->resetErrorBag(); $this->isUploadFormOpen = false; } public function saveImage(ImageService $imageService): void { $pressRelease = $this->getPressRelease(); $this->authorize('update', $pressRelease); if (!$this->canChangeImages($pressRelease)) { $this->addError('newImage', __('Das Titelbild kann nur bei Entwürfen oder abgelehnten PMs geändert werden.')); return; } if ($this->titleImageFor($pressRelease) !== null) { $this->addError('newImage', __('Bitte löschen Sie zuerst das vorhandene Titelbild.')); return; } $licenseType = ImageLicenseType::tryFrom($this->newLicenseType); $requiresLicenseUrl = $licenseType?->requiresLicenseUrl() ?? false; $requiresLicenseDetail = $licenseType?->requiresLicenseDetail() ?? false; $isAiGenerated = $licenseType?->isAiGenerated() ?? false; $this->validate( [ 'newImage' => ['required', 'image', 'mimes:jpeg,jpg,png,webp', 'max:' . (int) (ImageService::MAX_PRESS_RELEASE_IMAGE_BYTES / 1024)], 'newTitle' => ['nullable', 'string', 'max:120'], 'newCopyright' => ['required', 'string', 'max:255'], 'newAuthor' => ['required', 'string', 'max:255'], 'newLicenseType' => ['required', Rule::enum(ImageLicenseType::class)], 'newLicenseDetail' => [$requiresLicenseDetail ? 'required' : 'nullable', 'string', 'max:120'], 'newLicenseUrl' => [$requiresLicenseUrl ? 'required' : 'nullable', 'url', 'max:2048'], 'newSourceUrl' => ['nullable', 'url', 'max:2048'], 'newPeopleRightsStatus' => ['required', Rule::in(array_keys($this->peopleRightsOptions()))], 'newPropertyRightsStatus' => ['required', Rule::in(array_keys($this->propertyRightsOptions()))], 'newRightsNotes' => ['nullable', 'string', 'max:1000'], 'newRightsConfirmed' => ['accepted'], 'newAiTermsConfirmed' => [$isAiGenerated ? 'accepted' : 'nullable'], ], [ 'newCopyright.required' => __('Bitte einen öffentlichen Bildnachweis angeben, z. B. Foto: Max Mustermann / Beispiel GmbH.'), 'newAuthor.required' => $isAiGenerated ? __('Bitte angeben, wer für die Erstellung verantwortlich ist (Person oder Firma).') : __('Bitte Urheber, Fotograf oder Rechteinhaber angeben.'), 'newLicenseType.required' => __('Bitte einen Lizenztyp wählen.'), 'newLicenseDetail.required' => $isAiGenerated ? __('Bitte das verwendete KI-Tool angeben, z. B. Midjourney v7.') : __('Bitte die Lizenz genauer angeben.'), 'newLicenseUrl.required' => __('Für diesen Lizenztyp ist eine Nachweis-URL erforderlich.'), 'newPeopleRightsStatus.required' => __('Bitte angeben, ob erkennbare Personen abgebildet sind.'), 'newPropertyRightsStatus.required' => __('Bitte angeben, ob Marken, Kunstwerke oder private Orte sichtbar sind.'), 'newRightsConfirmed.accepted' => __('Bitte bestätigen, dass die Bildrechte geklärt sind.'), 'newAiTermsConfirmed.accepted' => __('Bitte bestätigen, dass die Anbieter-Bedingungen die kommerzielle Nutzung erlauben.'), ], ); $stored = $imageService->storePressReleaseImage($this->newImage, $pressRelease->id); $pressRelease->images()->update(['is_preview' => false]); $pressRelease->images()->create([ 'disk' => 'public', 'path' => $stored['path'], 'variants' => $stored['variants'], 'title' => $this->newTitle ?: null, 'copyright' => $this->newCopyright ?: null, 'author' => $this->newAuthor, 'license_type' => $this->newLicenseType, 'license_detail' => $this->newLicenseDetail ?: null, 'license_url' => $this->newLicenseUrl ?: null, 'source_url' => $this->newSourceUrl ?: null, 'persons_consent' => $this->newPeopleRightsStatus === 'consent', 'people_rights_status' => $this->newPeopleRightsStatus, 'property_rights_status' => $this->newPropertyRightsStatus, 'rights_notes' => $this->newRightsNotes ?: null, 'rights_confirmed_at' => now(), 'is_ai_generated' => $isAiGenerated, 'is_preview' => true, 'sort_order' => ((int) $pressRelease->images()->max('sort_order')) + 1, 'width' => $stored['width'], 'height' => $stored['height'], 'mime' => $stored['mime'], ]); $this->resetUploadForm(); $this->isUploadFormOpen = false; $this->dispatch('title-image-changed'); Flux::toast(text: __('Titelbild hochgeladen.'), variant: 'success'); } public function removeNewImage(): void { $this->reset('newImage'); $this->resetErrorBag('newImage'); } public function newImagePreviewUrl(): ?string { if (!is_object($this->newImage) || !method_exists($this->newImage, 'temporaryUrl')) { return null; } try { return $this->newImage->temporaryUrl(); } catch (\Throwable) { return null; } } public function remove(int $imageId, ImageService $imageService): void { $pressRelease = $this->getPressRelease(); $this->authorize('update', $pressRelease); if (!$this->canChangeImages($pressRelease)) { return; } $image = $pressRelease->images()->whereKey($imageId)->first(); if (!$image) { return; } $imageService->deletePressReleaseImage($image->disk, $image->path, $image->variants); $image->delete(); $this->dispatch('title-image-changed'); Flux::toast(text: __('Titelbild entfernt.'), variant: 'success'); } public function with(): array { $pressRelease = $this->getPressRelease(); return [ 'titleImage' => $this->titleImageFor($pressRelease), 'canEdit' => auth()->user()?->can('update', $pressRelease) === true && $this->canChangeImages($pressRelease), 'licenseTypeOptions' => ImageLicenseType::options(), 'ccLicenseOptions' => $this->ccLicenseOptions(), 'peopleRightsOptions' => $this->peopleRightsOptions(), 'propertyRightsOptions' => $this->propertyRightsOptions(), 'licenseUrlRequired' => ImageLicenseType::tryFrom($this->newLicenseType)?->requiresLicenseUrl() ?? false, 'licenseDetailRequired' => ImageLicenseType::tryFrom($this->newLicenseType)?->requiresLicenseDetail() ?? false, 'showsCcWarning' => $this->newLicenseType === ImageLicenseType::CreativeCommons->value, 'showsAiSection' => $this->newLicenseType === ImageLicenseType::AiGenerated->value, 'showsRightsWarning' => $this->shouldShowRightsWarning(), ]; } private function getPressRelease(): PressRelease { return PressRelease::withoutGlobalScopes()->findOrFail($this->pressReleaseId); } private function canChangeImages(PressRelease $pressRelease): bool { if (auth()->user()?->canAccessAdmin()) { return !in_array($pressRelease->status, [PressReleaseStatus::Archived], true); } return in_array($pressRelease->status, [PressReleaseStatus::Draft, PressReleaseStatus::Rejected], true); } private function titleImageFor(PressRelease $pressRelease): ?PressReleaseImage { return $pressRelease->images()->orderByDesc('is_preview')->orderBy('sort_order')->orderBy('id')->first(); } private function resetUploadForm(): void { $this->reset(['newImage', 'newTitle', 'newCopyright', 'newAuthor', 'newLicenseType', 'newLicenseDetail', 'newLicenseUrl', 'newSourceUrl', 'newPeopleRightsStatus', 'newPropertyRightsStatus', 'newRightsNotes', 'newRightsConfirmed', 'newAiTermsConfirmed']); } /** * @return array */ private function ccLicenseOptions(): array { return [ 'cc0' => 'CC0', 'cc_by' => 'CC BY', 'cc_by_sa' => 'CC BY-SA', 'cc_by_nd' => 'CC BY-ND', 'cc_by_nc' => 'CC BY-NC', 'cc_by_nc_sa' => 'CC BY-NC-SA', 'cc_by_nc_nd' => 'CC BY-NC-ND', ]; } /** * @return array */ private function peopleRightsOptions(): array { return [ 'none' => __('Nein, keine erkennbaren Personen'), 'consent' => __('Ja, Einwilligung liegt vor'), 'public_event' => __('Ja, öffentliche Veranstaltung / redaktioneller Kontext'), ]; } /** * @return array */ private function propertyRightsOptions(): array { return [ 'none' => __('Nein'), 'cleared' => __('Ja, Rechte / Nutzung sind geklärt'), ]; } private function shouldShowRightsWarning(): bool { $restrictedCcLicense = str_contains($this->newLicenseDetail, '_nc') || str_contains($this->newLicenseDetail, '_nd'); return $this->newLicenseType === ImageLicenseType::Other->value || $restrictedCcLicense; } }; ?>
{{ __('Titelbild') }} {{ $titleImage ? __('gesetzt') : __('Platzhalter aktiv') }}
@if ($titleImage)
@php $titleImageUrl = $titleImage->variantUrl('cover') ?? ($titleImage->variantUrl('large') ?? ($titleImage->variantUrl('medium') ?? $titleImage->url())); @endphp @if ($titleImageUrl) {{ $titleImage->title ?? __('Titelbild') }} @endif
{{ $titleImage->title ?: __('Eigenes Titelbild') }}
@if ($titleImage->author)

© {{ $titleImage->author }}

@endif @if ($titleImage->copyright)

{{ __('Bildnachweis: :copyright', ['copyright' => $titleImage->copyright]) }}

@endif
@if ($titleImage->is_ai_generated) {{ __('KI-generiert') }} @endif @if ($titleImage->license_type) {{ $titleImage->license_type->label() }} @endif @if ($titleImage->width && $titleImage->height) {{ $titleImage->width }}×{{ $titleImage->height }} @endif
@if ($canEdit) {{ __('Titelbild löschen') }} @endif
@elseif($canEdit) @if (!$isUploadFormOpen)
{{ __('Hier fehlt ein Titelbild') }} {{ __('Der Platzhalter bleibt aktiv, bis ein eigenes Titelbild hochgeladen wurde.') }}
{{ __('Eigenes Titelbild hochladen') }}
@else
{{ __('Titelbild hochladen') }} {{-- ===== Schritt 1 · Bild auswählen ===== --}}
1 {{ __('Bild auswählen') }}
@if (!$newImage)
{{ __('Bitte laden Sie nur Bilder hoch, für die Sie die erforderlichen Nutzungsrechte besitzen. Bilder aus Google, Social Media, Messenger-Gruppen oder fremden Websites dürfen nicht ohne ausdrückliche Erlaubnis verwendet werden.') }}
@else {{-- Große Vorschau im Titelbild-Format, damit das Motiv vor dem Hochladen wirklich beurteilbar ist. --}}
@if ($this->newImagePreviewUrl()) {{ __('Vorschau des gewählten Titelbilds') }} @else
@endif
{{ $newImage->getClientOriginalName() }} · {{ number_format($newImage->getSize() / 1048576, 2, ',', '.') }} MB
{{ __('Anderes Bild wählen') }}
@endif
{{-- ===== Schritt 2 · Öffentliche Bildinfos ===== --}}
2 {{ __('Bildinformationen (öffentlich sichtbar)') }}
{{-- ===== Schritt 3 · Herkunft & Lizenz ===== --}}
3 {{ __('Herkunft & Lizenz') }}
{{ __('Bitte wählen…') }} @foreach ($licenseTypeOptions as $value => $label) {{ $label }} @endforeach
@if ($showsAiSection)
{{ __('KI-generierte Bilder haben keinen menschlichen Urheber — maßgeblich sind die Nutzungsbedingungen des KI-Anbieters. Das Bild wird öffentlich als KI-generiert gekennzeichnet (Transparenzpflicht, EU AI Act). Achten Sie darauf, dass keine realen Personen, Marken oder geschützten Werke erkennbar nachgebildet werden.') }}
@elseif ($newLicenseType === \App\Enums\ImageLicenseType::CreativeCommons->value) {{ __('Bitte wählen…') }} @foreach ($ccLicenseOptions as $value => $label) {{ $label }} @endforeach
{{ __('Creative-Commons-Lizenzen können Einschränkungen enthalten. Bitte prüfen Sie, ob kommerzielle Nutzung, Bearbeitung und Veröffentlichung als Titelbild erlaubt sind.') }}
@elseif($licenseDetailRequired) @endif
{{-- ===== Schritt 4 · Personen & Rechte Dritter ===== --}}
4 {{ __('Personen & Rechte Dritter') }}
@foreach ($peopleRightsOptions as $value => $label) @endforeach @foreach ($propertyRightsOptions as $value => $label) @endforeach
@if (in_array($newPeopleRightsStatus, ['consent', 'public_event'], true))
{{ __('Bei erkennbaren Personen können zusätzlich Persönlichkeits- oder Datenschutzrechte betroffen sein. Bitte stellen Sie sicher, dass die Veröffentlichung zulässig ist.') }}
@endif @if ($showsRightsWarning)
{{ __('Diese Auswahl kann Einschränkungen enthalten. Bitte laden Sie das Bild nur hoch, wenn die Nutzung, Bearbeitung und Veröffentlichung als Titelbild erlaubt ist.') }}
@endif
{{-- ===== Schritt 5 · Bestätigung ===== --}}
5 {{ __('Bestätigung') }}
{{ __('Abbrechen') }} {{ __('Hochladen') }}
@endif @else
{{ __('Noch kein eigenes Titelbild hinterlegt. Der Platzhalter bleibt aktiv.') }}
@endif