original_disk ?? (string) config('images.originals_disk')); if ($image->original_path !== null && $disk->exists($image->original_path)) { $files['original.'.pathinfo($image->original_path, PATHINFO_EXTENSION)] = (string) $disk->get($image->original_path); } if ($image->published_snapshot_path !== null && $disk->exists($image->published_snapshot_path)) { $files['published.'.pathinfo($image->published_snapshot_path, PATHINFO_EXTENSION)] = (string) $disk->get($image->published_snapshot_path); $sidecar = preg_replace('/\.[a-z0-9]+$/i', '.json', $image->published_snapshot_path); if (is_string($sidecar) && $disk->exists($sidecar)) { $files['published.json'] = (string) $disk->get($sidecar); } } $pressRelease = $image->pressRelease; return $this->archive->record( $event, $image, $image->press_release_id, $reason, $this->actorType($actor), $actor?->getKey(), [ 'image' => $image->only([ 'id', 'press_release_id', 'path', 'variants', 'title', 'description', 'copyright', 'credit_source', 'author', 'license_url', 'source_url', 'ai_declaration', 'ai_declaration_user', 'ai_declaration_source', 'ai_declaration_locked', 'provenance_status', 'provenance', 'original_sha256', 'published_snapshot_sha256', 'rules_version', 'published_ever_at', 'withheld_at', 'created_at', ]), 'credit_line' => $pressRelease ? ImageCredit::for($image, $pressRelease->company)->line() : null, 'confirmations' => $image->rightsConfirmations()->orderBy('id')->get()->map(fn ($c) => $c->toArray())->all(), 'corrections' => ImageDeclarationCorrection::query()->where('press_release_image_id', $image->getKey())->orderBy('id')->get()->map(fn ($c) => $c->toArray())->all(), 'press_release' => $pressRelease ? $this->pressReleaseHeader($pressRelease) : null, ], $files, ['image_id' => $image->getKey(), 'files' => count($files)], ); } /** * Meldung aus dem öffentlichen Angebot genommen: letzter veröffentlichter * Stand mit Text, Bildern (Snapshots), Kontaktfeldern, Einreicher, Zeiten. */ public function pressReleaseDepublished(PressRelease $pressRelease, ArchiveReasonCategory $reason, ?User $actor = null, ?string $note = null): ?ArchiveEntry { $pressRelease->loadMissing(['company', 'user', 'contacts', 'images']); $files = []; $images = []; foreach ($pressRelease->images as $image) { $entry = [ 'id' => $image->getKey(), 'credit_line' => ImageCredit::for($image, $pressRelease->company)->line(), 'ai_declaration' => $image->ai_declaration?->value, 'ai_declaration_source' => $image->ai_declaration_source?->value, 'original_sha256' => $image->original_sha256, 'published_snapshot_sha256' => $image->published_snapshot_sha256, 'rules_version' => $image->rules_version, ]; if ($image->published_snapshot_path !== null) { $disk = Storage::disk($image->original_disk ?? (string) config('images.originals_disk')); if ($disk->exists($image->published_snapshot_path)) { $name = 'image-'.$image->getKey().'.'.pathinfo($image->published_snapshot_path, PATHINFO_EXTENSION); $files[$name] = (string) $disk->get($image->published_snapshot_path); $entry['file'] = $name; } } $images[] = $entry; } return $this->archive->record( ArchiveEvent::PressReleaseDepublished, $pressRelease, $pressRelease->getKey(), $reason, $this->actorType($actor), $actor?->getKey(), [ 'press_release' => $this->pressReleaseHeader($pressRelease) + $pressRelease->only([ 'subtitle', 'text', 'keywords', 'backlink_url', 'language', 'category_id', 'boilerplate_override', ]), 'contacts' => $pressRelease->contacts->map(fn ($contact) => $contact->only(['id', 'title', 'first_name', 'last_name', 'email', 'phone', 'responsibility']))->all(), 'images' => $images, 'note' => $note, ], $files, ['images' => count($images), 'files' => count($files)], ); } /** * DSGVO-Änderung (Schwärzung, Korrektur): Diff alt/neu mit Verweis auf * den Antrag. * * @param array $before * @param array $after */ public function gdprChange(PressRelease $pressRelease, array $before, array $after, ?LegalRequest $legalRequest, ?User $actor = null, ArchiveReasonCategory $reason = ArchiveReasonCategory::Gdpr, ?string $note = null): ?ArchiveEntry { return $this->archive->record( ArchiveEvent::GdprChange, $pressRelease, $pressRelease->getKey(), $reason, $this->actorType($actor), $actor?->getKey(), [ 'press_release' => $this->pressReleaseHeader($pressRelease), 'before' => $before, 'after' => $after, 'legal_request' => $legalRequest ? [ 'id' => $legalRequest->getKey(), 'reference' => $legalRequest->reference(), 'type' => $legalRequest->type->value, 'requester_email' => $legalRequest->requester_email, ] : null, 'note' => $note, ], [], ['fields' => count(array_keys($after)), 'legal_request_id' => $legalRequest?->getKey()], ); } /** * Korrekturweg (E.6): alter und neuer Wert, Quelle, Grund, Bearbeiter. */ public function labelCorrected(PressReleaseImage $image, ImageDeclarationCorrection $correction, ?User $actor = null): ?ArchiveEntry { return $this->archive->record( ArchiveEvent::LabelCorrected, $image, $image->press_release_id, ArchiveReasonCategory::Correction, $this->actorType($actor), $actor?->getKey(), [ 'correction' => $correction->toArray(), 'image' => $image->only(['id', 'copyright', 'ai_declaration', 'ai_declaration_user', 'ai_declaration_source', 'withheld_at']), ], [], ['correction_id' => $correction->getKey(), 'field' => $correction->field], ); } /** * @return array */ private function pressReleaseHeader(PressRelease $pressRelease): array { $pressRelease->loadMissing(['company' => fn ($q) => $q->withoutGlobalScope(PortalScope::class), 'user']); return [ 'id' => $pressRelease->getKey(), 'uuid' => $pressRelease->uuid, 'portal' => $pressRelease->portal->value, 'title' => $pressRelease->title, 'slug' => $pressRelease->slug, 'status' => $pressRelease->status->value, 'published_at' => $pressRelease->published_at?->toIso8601String(), 'published_ever_at' => $pressRelease->published_ever_at?->toIso8601String(), 'depublished_at' => $pressRelease->depublished_at?->toIso8601String(), 'company' => $pressRelease->company ? ['id' => $pressRelease->company->getKey(), 'name' => $pressRelease->company->name] : null, 'submitter' => $pressRelease->user ? ['id' => $pressRelease->user->getKey(), 'email' => $pressRelease->user->email, 'name' => $pressRelease->user->name] : null, ]; } private function actorType(?User $actor): string { if ($actor === null) { return 'system'; } return $actor->canAccessAdmin() ? 'admin' : 'customer'; } }