> */ public function forCase(User $actor, \stdClass $case, VirtualDesk $desk): array { $privateScope = ! $desk->owns($actor, $case, false) && ! $actor->can('manage-portal'); $documents = DB::table('desk_documents as l')->join('documents as d', 'd.id', '=', 'l.document_id')->where('l.case_id', $case->id)->whereNull('d.deleted_at') ->when($privateScope, fn ($q) => $q->where(fn ($p) => $p->whereNull('l.request_id')->orWhereIn('l.request_id', DB::table('desk_requests')->where('analyst_id', $actor->id)->select('id')))) ->get(['l.id as link_id', 'l.document_id', 'l.category', 'l.valid_until', 'd.file_name', 'd.version_no', 'd.sha256', 'd.status', 'd.malware_scan_status', 'd.malware_scanned_sha256', 'd.storage_key']); $reviews = DB::table('desk_events as e')->join('users as u', 'u.id', '=', 'e.actor_id')->where('e.case_id', $case->id)->where('e.action', 'document_reviewed') ->when($privateScope, fn ($q) => $q->where(fn ($p) => $p->whereNull('e.request_id')->orWhereIn('e.request_id', DB::table('desk_requests')->where('analyst_id', $actor->id)->select('id')))) ->orderByDesc('e.event_order')->get(['e.id', 'e.payload', 'e.created_at', 'e.actor_id', 'u.name']); $result = []; foreach (self::FIELDS as $field) { $records = []; foreach ($documents as $doc) { $latest = null; foreach ($reviews as $review) { $payload = $desk->decode($review->payload); if (($payload['link_id'] ?? null) === $doc->link_id) { $latest = ['id' => $review->id, 'author_id' => $review->actor_id, 'author' => $review->name, 'date' => $review->created_at, 'payload' => $payload]; break; } } if ($doc->category !== $field && ! ($latest && in_array($field, $latest['payload']['supports'] ?? [], true))) { continue; } // A later review replaces the earlier scope. Merely choosing a // document category does not verify a preparation assumption. if ($latest && ! in_array($field, $latest['payload']['supports'] ?? [], true)) { $latest = null; } $state = 'attached_unchecked'; if ($doc->valid_until && $doc->valid_until < now()->toDateString()) { $state = 'expired'; } elseif ($latest && (($latest['payload']['sha256'] ?? null) !== trim($doc->sha256) || ($latest['payload']['document_version'] ?? null) !== (int) $doc->version_no)) { $state = 'superseded'; } elseif ($latest) { $path = realpath(Storage::disk('local')->path($doc->storage_key)); $root = realpath(Storage::disk('local')->path('')); if (! $path || ! $root || ! str_starts_with($path, $root.DIRECTORY_SEPARATOR) || ! is_file($path) || ! is_string($hash = hash_file('sha256', $path)) || ! hash_equals(trim($doc->sha256), $hash)) { $state = 'superseded'; } elseif ($doc->status !== 'active' || $doc->malware_scan_status !== 'clean' || trim($doc->sha256) !== $doc->malware_scanned_sha256) { $state = 'technical_unavailable'; } else { $state = match ($latest['payload']['outcome'] ?? '') { 'internal_verified' => 'internally_verified', 'contradiction_identified' => 'contradiction', default => 'issue_identified', }; } } $records[] = ['document_id' => $doc->document_id, 'link_id' => $doc->link_id, 'name' => $doc->file_name, 'state' => $state, 'version' => (int) $doc->version_no, 'hash' => trim($doc->sha256), 'valid_until' => $doc->valid_until, 'review' => $latest ? ['event_id' => $latest['id'], 'author_id' => $latest['author_id'], 'author' => $latest['author'], 'date' => $latest['date'], 'what_checked' => $latest['payload']['what_checked'], 'evidence' => $latest['payload']['evidence'], 'scope' => $latest['payload']['scope']] : null]; } if ($records) { // A specific current contradiction takes precedence; other valid evidence // remains visible instead of erasing expired or replaced originals. $states = array_column($records, 'state'); $state = 'attached_unchecked'; foreach (['contradiction', 'internally_verified', 'issue_identified', 'superseded', 'expired', 'technical_unavailable'] as $candidate) { if (in_array($candidate, $states, true)) { $state = $candidate; break; } } $result[$field] = ['state' => $state, 'documents' => $records]; } } return $result; } }