$query * @return Builder */ public function apply(Builder $query, User $viewer): Builder { if (! $viewer->isClientScoped()) { return $query; } $fileMorph = (new File)->getMorphClass(); $folderMorph = (new Folder)->getMorphClass(); $userMorph = (new User)->getMorphClass(); $clientIds = $this->library->assignableClientIds($viewer) ?? []; return $query->where(function (Builder $outer) use ($viewer, $fileMorph, $folderMorph, $userMorph, $clientIds): void { $outer->where('actor_id', $viewer->id); $outer->orWhere(fn (Builder $files) => $files ->where('subject_type', $fileMorph) ->whereIn('subject_id', $this->library->files($viewer)->select('id'))); $outer->orWhere(fn (Builder $folders) => $folders ->where('subject_type', $folderMorph) ->whereIn('subject_id', $this->library->folders($viewer)->select('id'))); if ($clientIds !== []) { $outer->orWhere(fn (Builder $users) => $users ->where('subject_type', $userMorph) ->whereIn('subject_id', $clientIds)); } }); } /** * File ids from $ids the viewer may actually open — used to decide * whether a row links anywhere. Permission alone was the old test, * which produced links to files the viewer would get a 403 on. * * @param iterable $ids * @return array keyed by id; presence is the answer */ public function openableFileIds(User $viewer, iterable $ids): array { $ids = collect($ids)->filter()->unique(); if ($ids->isEmpty()) { return []; } return $this->library->files($viewer) ->whereIn('id', $ids) ->pluck('id') ->mapWithKeys(fn ($id): array => [(int) $id => true]) ->all(); } /** * @param iterable $ids * @return array keyed by id; presence is the answer */ public function openableFolderIds(User $viewer, iterable $ids): array { $ids = collect($ids)->filter()->unique(); if ($ids->isEmpty()) { return []; } return $this->library->folders($viewer) ->whereIn('id', $ids) ->pluck('id') ->mapWithKeys(fn ($id): array => [(int) $id => true]) ->all(); } }