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
2124ffd1f2
commit
46b89e1d23
24 changed files with 1202 additions and 12 deletions
62
app/Models/ImageDeclarationCorrection.php
Normal file
62
app/Models/ImageDeclarationCorrection.php
Normal 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');
|
||||
}
|
||||
}
|
||||
|
|
@ -104,6 +104,7 @@ class PressRelease extends Model
|
|||
'no_export',
|
||||
'published_at',
|
||||
'depublished_at',
|
||||
'published_ever_at',
|
||||
'gone_at',
|
||||
'no_export_before_gone',
|
||||
'scheduled_at',
|
||||
|
|
@ -134,6 +135,7 @@ class PressRelease extends Model
|
|||
'no_export' => 'boolean',
|
||||
'published_at' => 'datetime',
|
||||
'depublished_at' => 'datetime',
|
||||
'published_ever_at' => 'datetime',
|
||||
'gone_at' => 'datetime',
|
||||
'no_export_before_gone' => 'boolean',
|
||||
'sync_locked_at' => 'datetime',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use App\Enums\CreditSource;
|
|||
use App\Enums\ImageLicenseType;
|
||||
use App\Enums\ImageProvenanceStatus;
|
||||
use Database\Factories\PressReleaseImageFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -60,6 +61,7 @@ use Illuminate\Support\Facades\URL;
|
|||
* @property string|null $published_snapshot_sha256
|
||||
* @property Carbon|null $published_ever_at
|
||||
* @property Carbon|null $customer_confirmation_requested_at
|
||||
* @property Carbon|null $withheld_at
|
||||
* @property string|null $rules_version
|
||||
* @property int|null $focus_x
|
||||
* @property int|null $focus_y
|
||||
|
|
@ -116,6 +118,7 @@ class PressReleaseImage extends Model
|
|||
'published_snapshot_sha256',
|
||||
'published_ever_at',
|
||||
'customer_confirmation_requested_at',
|
||||
'withheld_at',
|
||||
'rules_version',
|
||||
'focus_x',
|
||||
'focus_y',
|
||||
|
|
@ -147,6 +150,7 @@ class PressReleaseImage extends Model
|
|||
'provenance_checked_at' => 'datetime',
|
||||
'published_ever_at' => 'datetime',
|
||||
'customer_confirmation_requested_at' => 'datetime',
|
||||
'withheld_at' => 'datetime',
|
||||
'focus_x' => 'integer',
|
||||
'focus_y' => 'integer',
|
||||
'no_crop' => 'boolean',
|
||||
|
|
@ -220,6 +224,26 @@ class PressReleaseImage extends Model
|
|||
return $this->published_ever_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vorläufig zurückgehalten (Korrekturweg E.6): öffentlich greift der
|
||||
* Platzhalter, im Panel bleibt das Bild sichtbar.
|
||||
*/
|
||||
public function isWithheld(): bool
|
||||
{
|
||||
return $this->withheld_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Öffentlich ausspielbare Bilder: nicht zurückgehalten.
|
||||
*
|
||||
* @param Builder<PressReleaseImage> $query
|
||||
* @return Builder<PressReleaseImage>
|
||||
*/
|
||||
public function scopePubliclyVisible(Builder $query): Builder
|
||||
{
|
||||
return $query->whereNull('withheld_at');
|
||||
}
|
||||
|
||||
public function url(bool $signed = false): ?string
|
||||
{
|
||||
if (blank($this->path)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue