Skip to content

Commit 4de39fa

Browse files
authored
Merge pull request #3821 from nextcloud/fix/vote-limit-check/next
Fix limit check
2 parents d2ed262 + 980f78b commit 4de39fa

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

lib/Controller/VoteController.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ public function list(int $pollId): JSONResponse {
5555
// #[FrontpageRoute(verb: 'PUT', url: '/vote/{optionId}/set/{setTo}')]
5656
public function set(int $optionId, string $setTo): JSONResponse {
5757
$option = $this->optionService->get($optionId);
58+
5859
return $this->response(fn () => [
5960
'vote' => $this->voteService->set($optionId, $setTo),
6061
'poll' => $this->pollService->get($option->getPollId()),
6162
'options' => $this->optionService->list($option->getPollId()),
62-
6363
]);
6464
}
6565

@@ -73,7 +73,12 @@ public function set(int $optionId, string $setTo): JSONResponse {
7373
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user/{userId}', postfix: 'named')]
7474
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user', postfix: 'self')]
7575
public function delete(int $pollId, string $userId = ''): JSONResponse {
76-
return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId)]);
76+
return $this->response(fn () => [
77+
'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId),
78+
'poll' => $this->pollService->get($pollId),
79+
'options' => $this->optionService->list($pollId),
80+
'votes' => $this->voteService->list($pollId)
81+
]);
7782
}
7883

7984
/**
@@ -85,6 +90,11 @@ public function delete(int $pollId, string $userId = ''): JSONResponse {
8590
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
8691
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/votes/orphaned')]
8792
public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse {
88-
return $this->response(fn () => ['deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true)]);
93+
return $this->response(fn () => [
94+
'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true),
95+
'poll' => $this->pollService->get($pollId),
96+
'options' => $this->optionService->list($pollId),
97+
'votes' => $this->voteService->list($pollId)
98+
]);
8999
}
90100
}

lib/Service/VoteService.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OCA\Polls\Db\Vote;
1616
use OCA\Polls\Db\VoteMapper;
1717
use OCA\Polls\Event\VoteSetEvent;
18+
use OCA\Polls\Exceptions\NotFoundException;
1819
use OCA\Polls\Exceptions\VoteLimitExceededException;
1920
use OCA\Polls\UserSession;
2021
use OCP\AppFramework\Db\DoesNotExistException;
@@ -65,6 +66,18 @@ private function checkLimits(Option $option): void {
6566
return;
6667
}
6768

69+
private function checkVoteLimit(Option $option): void {
70+
// check, if the optionlimit is reached or exceeded, if one is set
71+
if ($option->getIsLockedByOptionLimit()) {
72+
throw new VoteLimitExceededException();
73+
}
74+
75+
if ($option->getIsLockedByVotesLimit()) {
76+
throw new VoteLimitExceededException;
77+
}
78+
return;
79+
}
80+
6881
/**
6982
* Set vote
7083
*/
@@ -73,6 +86,11 @@ public function set(int $optionId, string $setTo): ?Vote {
7386
$poll = $this->pollMapper->find($option->getPollId());
7487
$poll->request(Poll::PERMISSION_VOTE_EDIT);
7588

89+
if ($option->getIsLocked()) {
90+
$this->checkVoteLimit($option);
91+
throw new NotFoundException();
92+
}
93+
7694
try {
7795
$this->vote = $this->voteMapper->findSingleVote($poll->getId(), $option->getPollOptionText(), $this->userSession->getCurrentUserId());
7896

src/components/VoteTable/VoteItem.vue

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,21 @@
6767
/**
6868
*
6969
*/
70-
function setVote() {
70+
async function setVote() {
7171
if (isVotable.value) {
72-
votesStore.set({
73-
option: props.option,
74-
setTo: nextAnswer.value,
75-
})
76-
showSuccess(t('polls', 'Vote saved'), { timeout: 2000 })
72+
try {
73+
await votesStore.set({
74+
option: props.option,
75+
setTo: nextAnswer.value,
76+
})
77+
showSuccess(t('polls', 'Vote saved'), { timeout: 2000 })
78+
} catch (error) {
79+
if (error.response.status === 409 && error.response.data.message === 'Vote limit exceeded') {
80+
showError(t('polls', 'Vote already booked out'))
81+
} else {
82+
showError(t('polls', 'Error saving vote'))
83+
}
84+
}
7785
} else {
7886
showError(t('polls', 'Error saving vote'))
7987
}

src/stores/votes.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const useVotesStore = defineStore('votes', {
8181
this.$reset()
8282
return
8383
}
84-
84+
8585
const votes: Vote[] = []
8686
response.data.votes.forEach((vote: Vote) => {
8787
if (vote.answer === Answer.Yes) {
@@ -104,7 +104,7 @@ export const useVotesStore = defineStore('votes', {
104104
throw error
105105
}
106106
},
107-
107+
108108
setItem(payload: { option: Option; vote: Vote }) {
109109
const index = this.list.findIndex((vote: Vote) =>
110110
vote.pollId === payload.option.pollId
@@ -116,7 +116,7 @@ export const useVotesStore = defineStore('votes', {
116116
}
117117
this.list.push(payload.vote)
118118
},
119-
119+
120120
async set(payload: { option: Option; setTo: Answer }) {
121121
const sessionStore = useSessionStore()
122122
const optionsStore = useOptionsStore()
@@ -135,17 +135,17 @@ export const useVotesStore = defineStore('votes', {
135135
} catch (error) {
136136
if (error?.code === 'ERR_CANCELED') return
137137
if (error.response.status === 409) {
138-
this.load()
139-
optionsStore.load()
140138
pollStore.load()
139+
throw error
141140
} else {
142-
Logger.error('Error setting vote', { error, payload })
141+
Logger.error('Error setting vote aa', { error, payload })
143142
throw error
144143
}
145144
}
146145
},
147-
146+
148147
async resetVotes() {
148+
Logger.debug('Resetting votes')
149149
const sessionStore = useSessionStore()
150150
try {
151151
let response = null
@@ -162,7 +162,7 @@ export const useVotesStore = defineStore('votes', {
162162
throw error
163163
}
164164
},
165-
165+
166166
async deleteUser(payload) {
167167
const sessionStore = useSessionStore()
168168
try {
@@ -178,21 +178,19 @@ export const useVotesStore = defineStore('votes', {
178178
async removeOrphanedVotes() {
179179
const sessionStore = useSessionStore()
180180
const pollStore = usePollStore()
181-
const optionsStore = useOptionsStore()
182181
try {
183182
if (sessionStore.route.name === 'publicVote') {
184183
await PublicAPI.removeOrphanedVotes(sessionStore.route.params.token)
185184
} else {
186185
await VotesAPI.removeOrphanedVotes(sessionStore.route.params.id)
187186
}
188187
pollStore.load()
189-
optionsStore.load()
190188
} catch (error) {
191189
if (error?.code === 'ERR_CANCELED') return
192190
Logger.error('Error deleting orphaned votes', { error })
193191
throw error
194192
}
195193
},
196-
194+
197195
},
198196
})

0 commit comments

Comments
 (0)