mirror of
https://github.com/projectsend/projectsend.git
synced 2026-10-03 12:54:18 +00:00
Upload into the folder you are in, on the staff Files page
Inside a folder, Upload opened the upload page with no folder, so every file landed at the top of the library and had to be moved. The client portal already carried the folder; the staff page now does the same. The upload page takes ?folder=, says "Uploading into <folder>", passes it to the upload, and sends a multi-file upload back to that folder. The same two checks as the portal's upload page: a folder outside the staff member's library is a 404, so the page never confirms it exists, and one they may not upload into is a 403. ChunkedUploadsController still checks the destination again when the upload starts. While searching, the button keeps uploading to the top, since results span folders. Reported by @lolgufdHD (#1801)
This commit is contained in:
@@ -62,7 +62,22 @@ class FilesController extends Controller
|
||||
$user = $request->user();
|
||||
assert($user !== null);
|
||||
|
||||
// Opened from inside a folder, the upload goes into it (#1801).
|
||||
// The same two checks the portal's upload page makes, with the
|
||||
// staff library in place of the client's: a folder this person
|
||||
// cannot see is a 404, one they may not upload into is a 403.
|
||||
// ChunkedUploadsController checks the destination again when the
|
||||
// upload starts, so this decides what the page offers, not what
|
||||
// is allowed.
|
||||
$folder = null;
|
||||
if ($request->integer('folder') > 0) {
|
||||
$folder = Folder::query()->find($request->integer('folder'));
|
||||
abort_if($folder === null || ! app(StaffLibraryScope::class)->allowsFolder($user, $folder), 404);
|
||||
abort_unless(Folder::uploadableBy($user, $folder), 403);
|
||||
}
|
||||
|
||||
return Inertia::render('files/create', [
|
||||
'folder' => $folder === null ? null : ['id' => $folder->id, 'name' => $folder->name],
|
||||
'max_file_size_mb' => app(Settings::class)->get(Setting::MaxFileSizeMb),
|
||||
'part_size_mb' => (int) config('projectsend.upload_part_size_mb'),
|
||||
'allowed_extensions' => app(UploadExtensionPolicy::class)->hintFor($user),
|
||||
|
||||
@@ -7,12 +7,14 @@ import { useTranslation } from '@/hooks/use-translation';
|
||||
import AppLayout from '@/layouts/app-layout';
|
||||
|
||||
interface FilesCreateProps {
|
||||
/** The folder the upload page was opened from, which uploads go into. */
|
||||
folder: { id: number; name: string } | null;
|
||||
max_file_size_mb: number;
|
||||
part_size_mb: number;
|
||||
allowed_extensions: string[] | null;
|
||||
}
|
||||
|
||||
export default function FilesCreate({ max_file_size_mb, part_size_mb, allowed_extensions }: FilesCreateProps) {
|
||||
export default function FilesCreate({ folder, max_file_size_mb, part_size_mb, allowed_extensions }: FilesCreateProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const breadcrumbs: BreadcrumbItem[] = [
|
||||
@@ -24,7 +26,8 @@ export default function FilesCreate({ max_file_size_mb, part_size_mb, allowed_ex
|
||||
if (fileIds.length === 1) {
|
||||
router.visit(route('files.edit', fileIds[0]));
|
||||
} else if (fileIds.length > 1) {
|
||||
router.visit(route('files.index'));
|
||||
// Back to where the upload started, so the new files are in view.
|
||||
router.visit(route('files.index', folder !== null ? { folder: folder.id } : {}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,7 +47,12 @@ export default function FilesCreate({ max_file_size_mb, part_size_mb, allowed_ex
|
||||
}
|
||||
/>
|
||||
|
||||
{folder !== null && (
|
||||
<p className="text-muted-foreground mb-4 text-sm">{t('Uploading into :folder', { folder: folder.name })}</p>
|
||||
)}
|
||||
|
||||
<ChunkedUploadDashboard
|
||||
folderId={folder?.id}
|
||||
maxFileSizeMb={max_file_size_mb}
|
||||
partSizeMb={part_size_mb}
|
||||
allowedExtensions={allowed_extensions}
|
||||
|
||||
@@ -397,7 +397,10 @@ export default function FilesIndex({
|
||||
)}
|
||||
{can_upload && (
|
||||
<Button asChild>
|
||||
<Link href={route('files.create')}>{t('Upload')}</Link>
|
||||
{/* Into the folder on screen, not the top of the
|
||||
library (#1801). Not while searching: the
|
||||
results span folders, so there is no "here". */}
|
||||
<Link href={route('files.create', folder !== null && !searching ? { folder: folder.id } : {})}>{t('Upload')}</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\User;
|
||||
use App\Modules\Files\Models\Folder;
|
||||
use App\Modules\Identity\Permissions\SystemRole;
|
||||
|
||||
/*
|
||||
* Upload from inside a folder on the staff Files page opened the upload
|
||||
* page with no folder, so everything landed at the top of the library
|
||||
* (#1801). The portal already carried the folder; this is the staff twin.
|
||||
*/
|
||||
|
||||
beforeEach(function () {
|
||||
$this->admin = User::factory()->create();
|
||||
});
|
||||
|
||||
test('the upload page opened from a folder uploads into that folder', function () {
|
||||
$folder = Folder::query()->create(['name' => 'Wedding']);
|
||||
|
||||
$this->actingAs($this->admin)->get(route('files.create', ['folder' => $folder->id]))
|
||||
->assertInertia(fn ($page) => $page
|
||||
->where('folder.id', $folder->id)
|
||||
->where('folder.name', 'Wedding'));
|
||||
});
|
||||
|
||||
test('without a folder it still uploads to the top of the library', function () {
|
||||
$this->actingAs($this->admin)->get(route('files.create'))
|
||||
->assertInertia(fn ($page) => $page->where('folder', null));
|
||||
});
|
||||
|
||||
test('a folder that does not exist is a 404, not a quiet upload to the top', function () {
|
||||
$this->actingAs($this->admin)->get(route('files.create', ['folder' => 999999]))->assertNotFound();
|
||||
});
|
||||
|
||||
test('a folder outside a client-scoped staff member\'s library is a 404', function () {
|
||||
// Nobody is assigned to this manager, so the folder is outside what
|
||||
// they can see. The page must not confirm it exists by naming it.
|
||||
$manager = User::factory()->role(SystemRole::ClientManager)->create();
|
||||
$folder = Folder::query()->create(['name' => 'Somebody else\'s']);
|
||||
|
||||
$this->actingAs($manager)->get(route('files.create', ['folder' => $folder->id]))->assertNotFound();
|
||||
});
|
||||
Reference in New Issue
Block a user