From 980f78bd1b8b86e6aee67e255a4eea6c359ee6a8 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Sat, 28 Dec 2024 10:41:39 +0100 Subject: [PATCH] Fix limit check #3758 Signed-off-by: dartcafe --- lib/Controller/VoteController.php | 16 +++++++++++++--- lib/Service/VoteService.php | 18 ++++++++++++++++++ src/components/VoteTable/VoteItem.vue | 20 ++++++++++++++------ src/stores/votes.ts | 20 +++++++++----------- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/lib/Controller/VoteController.php b/lib/Controller/VoteController.php index fcbee0f5c..e9a7575b8 100644 --- a/lib/Controller/VoteController.php +++ b/lib/Controller/VoteController.php @@ -55,11 +55,11 @@ public function list(int $pollId): JSONResponse { // #[FrontpageRoute(verb: 'PUT', url: '/vote/{optionId}/set/{setTo}')] public function set(int $optionId, string $setTo): JSONResponse { $option = $this->optionService->get($optionId); + return $this->response(fn () => [ 'vote' => $this->voteService->set($optionId, $setTo), 'poll' => $this->pollService->get($option->getPollId()), 'options' => $this->optionService->list($option->getPollId()), - ]); } @@ -73,7 +73,12 @@ public function set(int $optionId, string $setTo): JSONResponse { #[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user/{userId}', postfix: 'named')] #[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user', postfix: 'self')] public function delete(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId)]); + return $this->response(fn () => [ + 'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId), + 'poll' => $this->pollService->get($pollId), + 'options' => $this->optionService->list($pollId), + 'votes' => $this->voteService->list($pollId) + ]); } /** @@ -85,6 +90,11 @@ public function delete(int $pollId, string $userId = ''): JSONResponse { #[OpenAPI(OpenAPI::SCOPE_IGNORE)] #[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/votes/orphaned')] public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse { - return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]); + return $this->response(fn () => [ + 'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true), + 'poll' => $this->pollService->get($pollId), + 'options' => $this->optionService->list($pollId), + 'votes' => $this->voteService->list($pollId) + ]); } } diff --git a/lib/Service/VoteService.php b/lib/Service/VoteService.php index 927a3c592..9bb15e343 100644 --- a/lib/Service/VoteService.php +++ b/lib/Service/VoteService.php @@ -15,6 +15,7 @@ use OCA\Polls\Db\Vote; use OCA\Polls\Db\VoteMapper; use OCA\Polls\Event\VoteSetEvent; +use OCA\Polls\Exceptions\NotFoundException; use OCA\Polls\Exceptions\VoteLimitExceededException; use OCA\Polls\UserSession; use OCP\AppFramework\Db\DoesNotExistException; @@ -65,6 +66,18 @@ private function checkLimits(Option $option): void { return; } + private function checkVoteLimit(Option $option): void { + // check, if the optionlimit is reached or exceeded, if one is set + if ($option->getIsLockedByOptionLimit()) { + throw new VoteLimitExceededException(); + } + + if ($option->getIsLockedByVotesLimit()) { + throw new VoteLimitExceededException; + } + return; + } + /** * Set vote */ @@ -73,6 +86,11 @@ public function set(int $optionId, string $setTo): ?Vote { $poll = $this->pollMapper->find($option->getPollId()); $poll->request(Poll::PERMISSION_VOTE_EDIT); + if ($option->getIsLocked()) { + $this->checkVoteLimit($option); + throw new NotFoundException(); + } + try { $this->vote = $this->voteMapper->findSingleVote($poll->getId(), $option->getPollOptionText(), $this->userSession->getCurrentUserId()); diff --git a/src/components/VoteTable/VoteItem.vue b/src/components/VoteTable/VoteItem.vue index b9f2e842d..b2279d3e6 100644 --- a/src/components/VoteTable/VoteItem.vue +++ b/src/components/VoteTable/VoteItem.vue @@ -67,13 +67,21 @@ /** * */ - function setVote() { + async function setVote() { if (isVotable.value) { - votesStore.set({ - option: props.option, - setTo: nextAnswer.value, - }) - showSuccess(t('polls', 'Vote saved'), { timeout: 2000 }) + try { + await votesStore.set({ + option: props.option, + setTo: nextAnswer.value, + }) + showSuccess(t('polls', 'Vote saved'), { timeout: 2000 }) + } catch (error) { + if (error.response.status === 409 && error.response.data.message === 'Vote limit exceeded') { + showError(t('polls', 'Vote already booked out')) + } else { + showError(t('polls', 'Error saving vote')) + } + } } else { showError(t('polls', 'Error saving vote')) } diff --git a/src/stores/votes.ts b/src/stores/votes.ts index 374e73d52..8a05908c5 100644 --- a/src/stores/votes.ts +++ b/src/stores/votes.ts @@ -81,7 +81,7 @@ export const useVotesStore = defineStore('votes', { this.$reset() return } - + const votes: Vote[] = [] response.data.votes.forEach((vote: Vote) => { if (vote.answer === Answer.Yes) { @@ -104,7 +104,7 @@ export const useVotesStore = defineStore('votes', { throw error } }, - + setItem(payload: { option: Option; vote: Vote }) { const index = this.list.findIndex((vote: Vote) => vote.pollId === payload.option.pollId @@ -116,7 +116,7 @@ export const useVotesStore = defineStore('votes', { } this.list.push(payload.vote) }, - + async set(payload: { option: Option; setTo: Answer }) { const sessionStore = useSessionStore() const optionsStore = useOptionsStore() @@ -135,17 +135,17 @@ export const useVotesStore = defineStore('votes', { } catch (error) { if (error?.code === 'ERR_CANCELED') return if (error.response.status === 409) { - this.load() - optionsStore.load() pollStore.load() + throw error } else { - Logger.error('Error setting vote', { error, payload }) + Logger.error('Error setting vote aa', { error, payload }) throw error } } }, - + async resetVotes() { + Logger.debug('Resetting votes') const sessionStore = useSessionStore() try { let response = null @@ -162,7 +162,7 @@ export const useVotesStore = defineStore('votes', { throw error } }, - + async deleteUser(payload) { const sessionStore = useSessionStore() try { @@ -178,7 +178,6 @@ export const useVotesStore = defineStore('votes', { async removeOrphanedVotes() { const sessionStore = useSessionStore() const pollStore = usePollStore() - const optionsStore = useOptionsStore() try { if (sessionStore.route.name === 'publicVote') { await PublicAPI.removeOrphanedVotes(sessionStore.route.params.token) @@ -186,13 +185,12 @@ export const useVotesStore = defineStore('votes', { await VotesAPI.removeOrphanedVotes(sessionStore.route.params.id) } pollStore.load() - optionsStore.load() } catch (error) { if (error?.code === 'ERR_CANCELED') return Logger.error('Error deleting orphaned votes', { error }) throw error } }, - + }, })