From f65e8f07439d98db8a587bf4eee3253a1de0ec12 Mon Sep 17 00:00:00 2001 From: ildyria Date: Thu, 28 Nov 2024 18:17:33 +0100 Subject: [PATCH] fix copy move selection --- app/Actions/Album/ListAlbums.php | 17 +++++++++------ .../Controllers/Gallery/AlbumController.php | 10 +++------ .../Requests/Album/TargetListAlbumRequest.php | 21 ++++++++++++------- .../js/components/forms/album/AlbumMove.vue | 2 +- .../forms/album/SearchTargetAlbum.vue | 10 ++++----- .../gallery-dialogs/AlbumMergeDialog.vue | 3 ++- .../forms/gallery-dialogs/MoveDialog.vue | 10 ++++++++- .../forms/photo/PhotoCopyDialog.vue | 2 +- resources/js/services/album-service.ts | 4 ++-- tests/Feature_v2/Album/AlbumListTest.php | 6 +++--- 10 files changed, 51 insertions(+), 34 deletions(-) diff --git a/app/Actions/Album/ListAlbums.php b/app/Actions/Album/ListAlbums.php index ae68b1967ac..30d0d2eb2f4 100644 --- a/app/Actions/Album/ListAlbums.php +++ b/app/Actions/Album/ListAlbums.php @@ -19,21 +19,26 @@ class ListAlbums extends Action private const SHORTEN_BY = 80; /** - * @param int|null $lft - * @param int|null $rgt - * @param string|null $parent_id + * @param Collection $albumsFiltering + * @param string|null $parent_id * * @return TAlbumSaved[] */ - public function do(?int $lft, ?int $rgt, ?string $parent_id): array + public function do(Collection $albumsFiltering, ?string $parent_id): array { $albumQueryPolicy = resolve(AlbumQueryPolicy::class); $unfiltered = $albumQueryPolicy->applyReachabilityFilter( // We remove all sub albums // Otherwise it would create cyclic dependency Album::query() - ->when($lft !== null, - fn ($q) => $q->where('_lft', '<', $lft)->orWhere('_rgt', '>', $rgt)) + ->when($albumsFiltering->count() > 0, + function ($q) use ($albumsFiltering) { + $albumsFiltering->each( + fn ($a) => $q->whereNot(fn ($q1) => $q1->where('_lft', '>=', $a->_lft)->where('_rgt', '<=', $a->_rgt)) + ); + + return $q; + }) ); $sorting = AlbumSortingCriterion::createDefault(); $query = (new SortingDecorator($unfiltered)) diff --git a/app/Http/Controllers/Gallery/AlbumController.php b/app/Http/Controllers/Gallery/AlbumController.php index 0f94f404982..e9a83a0146e 100644 --- a/app/Http/Controllers/Gallery/AlbumController.php +++ b/app/Http/Controllers/Gallery/AlbumController.php @@ -191,14 +191,10 @@ public function delete(DeleteAlbumsRequest $request, Delete $delete): void */ public function getTargetListAlbums(TargetListAlbumRequest $request, ListAlbums $listAlbums) { - if ($request->album() instanceof Album) { - /** @var Album $album */ - $album = $request->album(); + $albums = $request->albums(); + $parent_id = $albums->count() > 0 ? $albums->first()->parent_id : null; - return TargetAlbumResource::collect($listAlbums->do($album->_lft, $album->_rgt, $album->parent_id)); - } - - return TargetAlbumResource::collect($listAlbums->do(null, null, null)); + return TargetAlbumResource::collect($listAlbums->do($albums, $parent_id)); } /** diff --git a/app/Http/Requests/Album/TargetListAlbumRequest.php b/app/Http/Requests/Album/TargetListAlbumRequest.php index 55f41e04db3..ff1af70dc63 100644 --- a/app/Http/Requests/Album/TargetListAlbumRequest.php +++ b/app/Http/Requests/Album/TargetListAlbumRequest.php @@ -2,20 +2,25 @@ namespace App\Http\Requests\Album; -use App\Contracts\Http\Requests\HasAbstractAlbum; +use App\Contracts\Http\Requests\HasAlbums; use App\Contracts\Http\Requests\RequestAttribute; use App\Contracts\Models\AbstractAlbum; use App\Exceptions\PasswordRequiredException; use App\Http\Requests\BaseApiRequest; -use App\Http\Requests\Traits\HasAbstractAlbumTrait; +use App\Http\Requests\Traits\HasAlbumsTrait; +use App\Models\Album; use App\Models\Extensions\BaseAlbum; use App\Policies\AlbumPolicy; use App\Rules\AlbumIDRule; use Illuminate\Support\Facades\Gate; -class TargetListAlbumRequest extends BaseApiRequest implements HasAbstractAlbum +/** + * @implements HasAlbums + */ +class TargetListAlbumRequest extends BaseApiRequest implements HasAlbums { - use HasAbstractAlbumTrait; + /** @phpstan-use HasAlbumsTrait */ + use HasAlbumsTrait; /** * {@inheritDoc} @@ -45,7 +50,8 @@ public function authorize(): bool public function rules(): array { return [ - RequestAttribute::ALBUM_ID_ATTRIBUTE => ['sometimes', new AlbumIDRule(false)], + RequestAttribute::ALBUM_IDS_ATTRIBUTE => 'sometimes|array|min:1', + RequestAttribute::ALBUM_IDS_ATTRIBUTE . '.*' => ['required', new AlbumIDRule(false)], ]; } @@ -54,7 +60,8 @@ public function rules(): array */ protected function processValidatedValues(array $values, array $files): void { - $album_id = $values[RequestAttribute::ALBUM_ID_ATTRIBUTE] ?? null; - $this->album = $this->albumFactory->findNullalbleAbstractAlbumOrFail($album_id); + $album_ids = $values[RequestAttribute::ALBUM_IDS_ATTRIBUTE] ?? []; + /** @phpstan-ignore-next-line */ + $this->albums = $this->albumFactory->findAbstractAlbumsOrFail($album_ids); } } diff --git a/resources/js/components/forms/album/AlbumMove.vue b/resources/js/components/forms/album/AlbumMove.vue index 77bdf4979fe..37db5519d30 100644 --- a/resources/js/components/forms/album/AlbumMove.vue +++ b/resources/js/components/forms/album/AlbumMove.vue @@ -9,7 +9,7 @@
{{ "Move to" }} - +

{{ "No album to move to" }}

diff --git a/resources/js/components/forms/album/SearchTargetAlbum.vue b/resources/js/components/forms/album/SearchTargetAlbum.vue index d643aaa23f2..52999174c2d 100644 --- a/resources/js/components/forms/album/SearchTargetAlbum.vue +++ b/resources/js/components/forms/album/SearchTargetAlbum.vue @@ -30,10 +30,10 @@ import AlbumService from "@/services/album-service"; import Select from "primevue/select"; const props = defineProps<{ - albumId: string | undefined; + albumIds: string[] | undefined; }>(); -const albumId = ref(props.albumId ?? null); +const albumIds = ref(props.albumIds ?? null); const emits = defineEmits<{ selected: [target: App.Http.Resources.Models.TargetAlbumResource]; "no-target": []; @@ -43,7 +43,7 @@ const options = ref const selectedTarget = ref(undefined); function load() { - AlbumService.getTargetListAlbums(albumId.value).then((response) => { + AlbumService.getTargetListAlbums(albumIds.value).then((response) => { options.value = response.data; if (options.value.length === 0) { emits("no-target"); @@ -62,9 +62,9 @@ function selected() { } watch( - () => props.albumId, + () => props.albumIds, (newAlbumId, _oldAlbumId) => { - albumId.value = newAlbumId ?? null; + albumIds.value = newAlbumId ?? null; load(); }, ); diff --git a/resources/js/components/forms/gallery-dialogs/AlbumMergeDialog.vue b/resources/js/components/forms/gallery-dialogs/AlbumMergeDialog.vue index 4a704a657af..edc72f1de7d 100644 --- a/resources/js/components/forms/gallery-dialogs/AlbumMergeDialog.vue +++ b/resources/js/components/forms/gallery-dialogs/AlbumMergeDialog.vue @@ -20,7 +20,7 @@ {{ sprintf("Merge %d albums to:", props.albumIds?.length) }} - +

{{ "No album to merge to." }}

@@ -58,6 +58,7 @@ const toast = useToast(); const titleMovedTo = ref(undefined); const destination_id = ref(undefined); const error_no_target = ref(false); + const confirmation = computed(() => { if (props.album) { return sprintf(trans("lychee.ALBUM_MERGE"), props.album.title, titleMovedTo.value); diff --git a/resources/js/components/forms/gallery-dialogs/MoveDialog.vue b/resources/js/components/forms/gallery-dialogs/MoveDialog.vue index b4188c6a6b2..d17af9346d6 100644 --- a/resources/js/components/forms/gallery-dialogs/MoveDialog.vue +++ b/resources/js/components/forms/gallery-dialogs/MoveDialog.vue @@ -17,7 +17,7 @@ {{ question }} - +

{{ "No album to move to." }}

@@ -70,6 +70,14 @@ function close() { visible.value = false; } +const headIds = computed(() => { + if (props.photo || (props.photoIds && props.photoIds?.length > 0)) { + return undefined; + } + + return props.albumIds; +}); + const question = computed(() => { if (props.photoIds && props.photoIds?.length > 1) { return sprintf("Move %d photos to:", props.photoIds?.length); diff --git a/resources/js/components/forms/photo/PhotoCopyDialog.vue b/resources/js/components/forms/photo/PhotoCopyDialog.vue index 4ff4c3a578f..ff85772c674 100644 --- a/resources/js/components/forms/photo/PhotoCopyDialog.vue +++ b/resources/js/components/forms/photo/PhotoCopyDialog.vue @@ -17,7 +17,7 @@ {{ question }} - +

{{ "No album to copy to." }}

diff --git a/resources/js/services/album-service.ts b/resources/js/services/album-service.ts index 46cd1119ce6..ad760a24fa7 100644 --- a/resources/js/services/album-service.ts +++ b/resources/js/services/album-service.ts @@ -114,8 +114,8 @@ const AlbumService = { return axios.delete(`${Constants.getApiUrl()}Album`, { data: { album_ids: album_ids } }); }, - getTargetListAlbums(album_id: string | null): Promise> { - return axios.get(`${Constants.getApiUrl()}Album::getTargetListAlbums`, { params: { album_id: album_id }, data: {} }); + getTargetListAlbums(album_ids: string[] | null): Promise> { + return axios.get(`${Constants.getApiUrl()}Album::getTargetListAlbums`, { params: { album_ids: album_ids }, data: {} }); }, move(dest: string | null, album_ids: string[]): Promise { diff --git a/tests/Feature_v2/Album/AlbumListTest.php b/tests/Feature_v2/Album/AlbumListTest.php index 2495c900d34..56a4a53b9ca 100644 --- a/tests/Feature_v2/Album/AlbumListTest.php +++ b/tests/Feature_v2/Album/AlbumListTest.php @@ -18,16 +18,16 @@ class AlbumListTest extends BaseApiV2Test { public function testListTargetAlbumUnauthorizedForbidden(): void { - $response = $this->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id); + $response = $this->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id); $this->assertUnauthorized($response); - $response = $this->actingAs($this->userLocked)->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id); + $response = $this->actingAs($this->userLocked)->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id); $this->assertForbidden($response); } public function testListTargetAlbumAuthorizedOwner(): void { - $response = $this->actingAs($this->admin)->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id); + $response = $this->actingAs($this->admin)->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id); $this->assertOk($response); $response->assertDontSee($this->subAlbum1->id); }