0, 'failed' => 0]; $since = $pressRelease->archive_pending_at; if ($since === null) { return $result; } $snapshots = PressReleaseRedactionSnapshot::query() ->where('press_release_id', $pressRelease->getKey()) ->where('created_at', '>=', $since->copy()->subMinute()) ->with('legalRequest') ->orderBy('id') ->get(); $this->attempt($result, fn () => $this->retryDepublished($pressRelease, $since)); foreach ($snapshots as $snapshot) { $this->attempt($result, fn () => $this->retryGdprChange($pressRelease, $snapshot)); } $latestRequest = $snapshots->last()?->legalRequest; foreach ($this->trashedImagesWithoutEntry($pressRelease) as $image) { $this->attempt($result, fn () => $this->remover->archiveTrashed( $image, null, ArchiveEvent::ImageRemoved, $latestRequest !== null ? ArchiveReasonCategory::Gdpr : ArchiveReasonCategory::OwnDecision, $latestRequest, 'Nachgeholt durch archive:retry-pending', )); } if ($result['failed'] === 0) { $pressRelease->forceFill(['archive_pending_at' => null])->save(); } return $result; } /** * @param array{recorded: int, failed: int} $result * @param callable(): ?ArchiveEntry $step */ private function attempt(array &$result, callable $step): void { try { $entry = $step(); } catch (Throwable $exception) { $result['failed']++; Log::error('Beweisarchiv: Nachholen eines Eintrags fehlgeschlagen.', ['error' => $exception->getMessage()]); return; } if ($entry === null) { return; } $result['recorded']++; } private function retryDepublished(PressRelease $pressRelease, \DateTimeInterface $since): ?ArchiveEntry { if ($pressRelease->status === PressReleaseStatus::Published || $pressRelease->published_ever_at === null) { return null; } $exists = ArchiveEntry::query() ->where('press_release_id', $pressRelease->getKey()) ->where('event', ArchiveEvent::PressReleaseDepublished->value) ->where('created_at', '>=', $since) ->exists(); if ($exists) { return null; } $pressRelease->loadMissing(['company' => fn ($q) => $q->withoutGlobalScope(PortalScope::class)]); return $this->archiver->pressReleaseDepublished( $pressRelease, $pressRelease->depublished_at !== null ? ArchiveReasonCategory::CustomerRequest : ArchiveReasonCategory::OwnDecision, null, 'Nachgeholt durch archive:retry-pending', ); } private function retryGdprChange(PressRelease $pressRelease, PressReleaseRedactionSnapshot $snapshot): ?ArchiveEntry { $exists = ArchiveEntry::query() ->where('press_release_id', $pressRelease->getKey()) ->where('event', ArchiveEvent::GdprChange->value) ->get(['id', 'summary']) ->contains(fn (ArchiveEntry $entry): bool => (int) ($entry->summary['redaction_snapshot_id'] ?? 0) === $snapshot->getKey()); if ($exists) { return null; } $before = (array) ($snapshot->encrypted_before['fields'] ?? []); $legalRequest = $snapshot->legalRequest; return $this->archiver->gdprChange( $pressRelease, $before, $pressRelease->only(array_keys($before)), $legalRequest, null, $legalRequest?->type === LegalRequestType::Correction ? ArchiveReasonCategory::Correction : ArchiveReasonCategory::Gdpr, 'Nachgeholt durch archive:retry-pending', $snapshot->getKey(), ); } /** * @return Collection */ private function trashedImagesWithoutEntry(PressRelease $pressRelease): Collection { $morph = (new PressReleaseImage)->getMorphClass(); return PressReleaseImage::withTrashed() ->where('press_release_id', $pressRelease->getKey()) ->whereNotNull('deleted_at') ->whereNotNull('published_ever_at') ->whereNotNull('rules_version') ->get() ->reject(fn (PressReleaseImage $image): bool => ArchiveEntry::query() ->where('subject_type', $morph) ->where('subject_id', $image->getKey()) ->whereIn('event', [ArchiveEvent::ImageRemoved->value, ArchiveEvent::ImageReplaced->value]) ->exists()); } }