KI-generierte Bilder: eigener Lizenztyp, Anbieter-Bestätigung, Kennzeichnung
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6e0b2b1814
commit
cc7b3c3379
9 changed files with 255 additions and 31 deletions
|
|
@ -204,6 +204,19 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<flux:icon.photo variant="micro" class="size-3.5" />
|
||||
<span>{{ __('Platzhalter-Titelbild (kein eigenes Bild hochgeladen).') }}</span>
|
||||
</div>
|
||||
@else
|
||||
@php
|
||||
$adminTitleImage = $pr->images->sortByDesc('is_preview')->first();
|
||||
@endphp
|
||||
@if ($adminTitleImage && ($adminTitleImage->copyright || $adminTitleImage->is_ai_generated))
|
||||
{{-- Bildnachweis + KI-Kennzeichnung (Art. 50 EU AI Act) --}}
|
||||
<div class="flex flex-wrap items-center gap-2 border-t border-[color:var(--color-bg-rule)] px-5 py-2.5 text-[12px] text-[color:var(--color-ink-3)]">
|
||||
@if ($adminTitleImage->is_ai_generated)
|
||||
<span class="badge hub">{{ __('KI-generiert') }}</span>
|
||||
@endif
|
||||
<span>{{ $adminTitleImage->copyright ?? __('Bild: KI-generiert') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</article>
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ new class extends Component {
|
|||
|
||||
public bool $newRightsConfirmed = false;
|
||||
|
||||
public bool $newAiTermsConfirmed = false;
|
||||
|
||||
public bool $isUploadFormOpen = false;
|
||||
|
||||
public function mount(int $pressReleaseId): void
|
||||
|
|
@ -62,12 +64,40 @@ new class extends Component {
|
|||
/**
|
||||
* 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".
|
||||
* „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
|
||||
|
|
@ -98,6 +128,7 @@ new class extends Component {
|
|||
$licenseType = ImageLicenseType::tryFrom($this->newLicenseType);
|
||||
$requiresLicenseUrl = $licenseType?->requiresLicenseUrl() ?? false;
|
||||
$requiresLicenseDetail = $licenseType?->requiresLicenseDetail() ?? false;
|
||||
$isAiGenerated = $licenseType?->isAiGenerated() ?? false;
|
||||
|
||||
$this->validate(
|
||||
[
|
||||
|
|
@ -113,16 +144,22 @@ new class extends Component {
|
|||
'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' => __('Bitte Urheber, Fotograf oder Rechteinhaber angeben.'),
|
||||
'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' => __('Bitte die Lizenz genauer angeben.'),
|
||||
'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.'),
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -146,6 +183,7 @@ new class extends Component {
|
|||
'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'],
|
||||
|
|
@ -217,6 +255,7 @@ new class extends Component {
|
|||
'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(),
|
||||
];
|
||||
}
|
||||
|
|
@ -242,7 +281,7 @@ new class extends Component {
|
|||
|
||||
private function resetUploadForm(): void
|
||||
{
|
||||
$this->reset(['newImage', 'newTitle', 'newCopyright', 'newAuthor', 'newLicenseType', 'newLicenseDetail', 'newLicenseUrl', 'newSourceUrl', 'newPeopleRightsStatus', 'newPropertyRightsStatus', 'newRightsNotes', 'newRightsConfirmed']);
|
||||
$this->reset(['newImage', 'newTitle', 'newCopyright', 'newAuthor', 'newLicenseType', 'newLicenseDetail', 'newLicenseUrl', 'newSourceUrl', 'newPeopleRightsStatus', 'newPropertyRightsStatus', 'newRightsNotes', 'newRightsConfirmed', 'newAiTermsConfirmed']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -331,6 +370,9 @@ new class extends Component {
|
|||
</p>
|
||||
@endif
|
||||
<div class="flex flex-wrap items-center gap-1 text-xs text-zinc-400">
|
||||
@if ($titleImage->is_ai_generated)
|
||||
<flux:badge color="purple" size="xs">{{ __('KI-generiert') }}</flux:badge>
|
||||
@endif
|
||||
@if ($titleImage->license_type)
|
||||
<flux:badge color="zinc" size="xs">{{ $titleImage->license_type->label() }}
|
||||
</flux:badge>
|
||||
|
|
@ -449,7 +491,8 @@ new class extends Component {
|
|||
class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Herkunft & Lizenz') }}</span>
|
||||
</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<flux:input wire:model="newAuthor" :label="__('Urheber / Fotograf / Rechteinhaber')"
|
||||
<flux:input wire:model="newAuthor"
|
||||
:label="$showsAiSection ? __('Verantwortlich für die Erstellung (Person/Firma)') : __('Urheber / Fotograf / Rechteinhaber')"
|
||||
:badge="__('Pflicht')" required />
|
||||
<flux:select wire:model.live="newLicenseType" :label="__('Lizenztyp')" :badge="__('Pflicht')"
|
||||
required>
|
||||
|
|
@ -460,7 +503,20 @@ new class extends Component {
|
|||
</flux:select>
|
||||
</div>
|
||||
|
||||
@if ($newLicenseType === \App\Enums\ImageLicenseType::CreativeCommons->value)
|
||||
@if ($showsAiSection)
|
||||
<flux:input wire:model.live.debounce.400ms="newLicenseDetail" :label="__('Verwendetes KI-Tool')"
|
||||
:badge="__('Pflicht')" placeholder="{{ __('z. B. Midjourney v7, DALL·E 3, Adobe Firefly') }}"
|
||||
required />
|
||||
|
||||
<div
|
||||
class="rounded-md border border-amber-200 bg-amber-50 p-3 text-xs leading-relaxed text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100">
|
||||
{{ __('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.') }}
|
||||
</div>
|
||||
|
||||
<flux:switch wire:model="newAiTermsConfirmed" align="right"
|
||||
:label="__('Anbieter-Bedingungen geprüft')"
|
||||
:description="__('Ich bestätige, dass die Nutzungsbedingungen des KI-Anbieters die kommerzielle Nutzung und Veröffentlichung dieses Bildes erlauben.')" />
|
||||
@elseif ($newLicenseType === \App\Enums\ImageLicenseType::CreativeCommons->value)
|
||||
<flux:select wire:model.live="newLicenseDetail" :label="__('Creative-Commons-Lizenz')"
|
||||
required>
|
||||
<flux:select.option value="" disabled>{{ __('Bitte wählen…') }}</flux:select.option>
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
'categoryName' => $categoryName,
|
||||
'coverUrl' => $cover->coverUrl($pr, 'cover'),
|
||||
'coverIsPlaceholder' => $cover->coverIsPlaceholder($pr),
|
||||
'titleImage' => $pr->images()->orderByDesc('is_preview')->orderBy('sort_order')->orderBy('id')->first(),
|
||||
'quotaTotal' => $user->pressReleaseQuotaTotal(),
|
||||
'quotaRemaining' => $user->pressReleaseQuotaRemaining(),
|
||||
'canEdit' => auth()->user()->can('update', $pr)
|
||||
|
|
@ -505,6 +506,14 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<flux:icon.photo variant="micro" class="size-3.5" />
|
||||
<span>{{ __('Platzhalter-Titelbild — laden Sie im Editor ein eigenes Bild hoch.') }}</span>
|
||||
</div>
|
||||
@elseif ($titleImage && ($titleImage->copyright || $titleImage->is_ai_generated))
|
||||
{{-- Bildnachweis + KI-Kennzeichnung (Art. 50 EU AI Act) --}}
|
||||
<div class="flex flex-wrap items-center gap-2 border-t border-[color:var(--color-bg-rule)] px-5 py-2.5 text-[12px] text-[color:var(--color-ink-3)]">
|
||||
@if ($titleImage->is_ai_generated)
|
||||
<span class="badge hub">{{ __('KI-generiert') }}</span>
|
||||
@endif
|
||||
<span>{{ $titleImage->copyright ?? __('Bild: KI-generiert') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</article>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue