mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-18 01:25:09 +00:00
6e47d76ba6
Client file sharing, rebuilt from the ground up: a private area per client, resumable uploads, folders, groups and categories, sharing with expiry dates and download limits, comments, file versions, an activity log, a REST API, and sixteen languages. This repository begins here. ProjectSend 2 was developed privately, and that development history is not published — the previous generation remains available, with its own history, at projectsend/legacy. Free software under the GNU General Public License v2, or (at your option) any later version.
80 lines
2.4 KiB
PHP
80 lines
2.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Files\Folders;
|
|
|
|
use App\Modules\Files\Models\Folder;
|
|
|
|
/**
|
|
* The trail from the library root down to a folder.
|
|
*
|
|
* The staff library and the client portal both render one, and both built it
|
|
* the same way: collect the ancestor ids, fetch them in one query, then put
|
|
* them back into ancestor order — because whereIn() returns rows in whatever
|
|
* order it likes, and a breadcrumb in the wrong order is nonsense. The
|
|
* reordering is the fiddly part, and the part worth having once.
|
|
*/
|
|
class BreadcrumbBuilder
|
|
{
|
|
/**
|
|
* The full trail to $current, root first.
|
|
*
|
|
* @return list<array{id: int, name: string}>
|
|
*/
|
|
public function for(?Folder $current): array
|
|
{
|
|
if ($current === null) {
|
|
return [];
|
|
}
|
|
|
|
return $this->fromIds([...$current->ancestorIds(), $current->id]);
|
|
}
|
|
|
|
/**
|
|
* The trail to $current, trimmed to what this viewer can actually see:
|
|
* it starts at the first visible ancestor, since a client shared with a
|
|
* folder deep in the tree has no business seeing the names of the
|
|
* folders above it.
|
|
*
|
|
* @param list<int> $visibleIds folder ids within the viewer's subtree
|
|
* @return list<array{id: int, name: string}>
|
|
*/
|
|
public function visible(?Folder $current, array $visibleIds): array
|
|
{
|
|
if ($current === null) {
|
|
return [];
|
|
}
|
|
|
|
$ids = [...$current->ancestorIds(), $current->id];
|
|
|
|
foreach ($ids as $index => $id) {
|
|
if (in_array($id, $visibleIds, true)) {
|
|
return $this->fromIds(array_slice($ids, $index));
|
|
}
|
|
}
|
|
|
|
// Nothing on the path is visible — show the folder alone rather than
|
|
// leaking the names of ancestors this viewer cannot reach.
|
|
return [['id' => $current->id, 'name' => $current->name]];
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $ids in ancestor order, root first
|
|
* @return list<array{id: int, name: string}>
|
|
*/
|
|
private function fromIds(array $ids): array
|
|
{
|
|
$folders = Folder::query()->whereIn('id', $ids)->get()
|
|
->sortBy(fn (Folder $folder): int => (int) array_search($folder->id, $ids, true));
|
|
|
|
$trail = [];
|
|
|
|
foreach ($folders as $folder) {
|
|
$trail[] = ['id' => $folder->id, 'name' => $folder->name];
|
|
}
|
|
|
|
return $trail;
|
|
}
|
|
}
|