Skip to content

Commit 664b757

Browse files
authored
Merge pull request #4090 from nextcloud/enh/poll-groups
Adding poll groups
2 parents 33663bc + c8d1016 commit 664b757

23 files changed

Lines changed: 1491 additions & 121 deletions

lib/Controller/PageController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public function __construct(
4444
#[FrontpageRoute(verb: 'GET', url: '/combo', postfix: 'combo')]
4545
#[FrontpageRoute(verb: 'GET', url: '/not-found', postfix: 'notFound')]
4646
#[FrontpageRoute(verb: 'GET', url: '/list/{category}', postfix: 'list')]
47+
#[FrontpageRoute(verb: 'GET', url: '/group/{slug}', postfix: 'group')]
4748
public function index(): TemplateResponse {
4849
Util::addScript(AppConstants::APP_ID, 'polls-main');
4950
$this->eventDispatcher->dispatchTyped(new LoadAdditionalScriptsEvent());

lib/Controller/PollApiController.php

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,83 @@ public function __construct(
4343

4444
/**
4545
* Get list of polls
46+
*
47+
* psalm-return DataResponse<array{polls: PollsPoll[]}>
4648
*/
4749
#[CORS]
4850
#[NoAdminRequired]
4951
#[NoCSRFRequired]
5052
#[ApiRoute(verb: 'GET', url: '/api/v1.0/polls', requirements: ['apiVersion' => '(v2)'])]
51-
public function list(): DataResponse {
52-
return $this->response(fn () => ['polls' => $this->pollService->list()]);
53+
public function listPolls(): DataResponse {
54+
return $this->response(fn () => ['polls' => $this->pollService->listPolls()]);
55+
}
56+
57+
/**
58+
* Get list of pollgroups
59+
*
60+
* psalm-return DataResponse<array{ pollGroups: array<int, PollGroup> }>
61+
*/
62+
#[CORS]
63+
#[NoAdminRequired]
64+
#[NoCSRFRequired]
65+
#[ApiRoute(verb: 'GET', url: '/api/v1.0/pollgroups', requirements: ['apiVersion' => '(v2)'])]
66+
public function listPollGroups(): DataResponse {
67+
return $this->response(fn () => ['pollGroups' => $this->pollService->listPollGroups()]);
68+
}
69+
70+
/**
71+
* Create a new pollgroup with its title and add a poll to it
72+
*
73+
* @param int $pollId Poll id to add to the new pollgroup
74+
* @param string $newPollGroupName Name of the new pollgroup
75+
*
76+
* psalm-return JSONResponse<array{pollGroup: PollGroup, poll: Poll}>
77+
*/
78+
#[CORS]
79+
#[NoAdminRequired]
80+
#[NoCSRFRequired]
81+
#[ApiRoute(verb: 'POST', url: '/api/v1.0/pollgroup/new/poll/{pollId}', requirements: ['apiVersion' => '(v2)'])]
82+
public function addPollToNewPollGroup(int $pollId, string $newPollGroupName = ''): DataResponse {
83+
return $this->response(fn () => [
84+
'pollGroup' => $this->pollService->addPollToPollGroup($pollId, newPollGroupName: $newPollGroupName),
85+
'poll' => $this->pollService->get($pollId),
86+
]);
87+
}
88+
89+
/**
90+
* Add poll to a group
91+
* @param int $pollGroupId Pollgroup id
92+
* @param int $pollId Poll id
93+
*
94+
* psalm-return DataResponse<array{ pollGroup: PollGroup, poll: Poll }>
95+
*/
96+
#[CORS]
97+
#[NoAdminRequired]
98+
#[NoCSRFRequired]
99+
#[ApiRoute(verb: 'PUT', url: '/api/v1.0/pollgroup/{pollGroupId}/poll/{pollId}', requirements: ['apiVersion' => '(v2)'])]
100+
public function addPollToPollGroup(int $pollId, int $pollGroupId): DataResponse {
101+
return $this->response(fn () => [
102+
'pollGroup' => $this->pollService->addPollToPollGroup($pollId, $pollGroupId),
103+
'poll' => $this->pollService->get($pollId),
104+
]);
105+
}
106+
107+
/**
108+
* Remove poll from a group
109+
* @param int $pollGroupId Pollgroup id
110+
* @param int $pollId Poll id
111+
*
112+
* psalm-return DataResponse<array{ pollgroup: PollGroup | null, poll: Poll }>
113+
*/
114+
#[CORS]
115+
#[NoAdminRequired]
116+
#[NoCSRFRequired]
117+
#[ApiRoute(verb: 'DELETE', url: '/api/v1.0/pollgroup/{pollGroupId}/poll/{pollId}', requirements: ['apiVersion' => '(v2)'])]
118+
public function removePollFromPollGroup(int $pollId, int $pollGroupId): DataResponse {
119+
return $this->response(fn () => [
120+
'pollgroup' => $this->pollService->removePollFromPollGroup($pollId, $pollGroupId),
121+
'poll' => $this->pollService->get($pollId),
122+
]);
53123
}
54124

55125
/**

lib/Controller/PollController.php

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,36 @@ public function __construct(
4444

4545
/**
4646
* Get list of polls
47+
* psalm-return JSONResponse<array{
48+
* polls: array<int, Poll>,
49+
* permissions: array{
50+
* pollCreationAllowed: bool,
51+
* comboAllowed: bool
52+
* },
53+
* pollGroups: array<int, PollGroup>
54+
* }>
4755
*/
4856
#[NoAdminRequired]
4957
#[FrontpageRoute(verb: 'GET', url: '/polls')]
50-
public function list(): JSONResponse {
58+
public function listPolls(): JSONResponse {
5159
return $this->response(function () {
5260
$appSettings = Container::queryClass(AppSettings::class);
5361
return [
54-
'list' => $this->pollService->list(),
62+
'polls' => $this->pollService->listPolls(),
5563
'permissions' => [
5664
'pollCreationAllowed' => $appSettings->getPollCreationAllowed(),
5765
'comboAllowed' => $appSettings->getComboAllowed(),
5866
],
67+
'pollGroups' => $this->pollService->listPollGroups(),
5968
];
6069
});
6170
}
6271

6372
/**
6473
* get poll
6574
* @param int $pollId Poll id
75+
*
76+
* psalm-return JSONResponse<array{poll: Poll}>
6677
*/
6778
#[NoAdminRequired]
6879
#[FrontpageRoute(verb: 'GET', url: '/poll/{pollId}/poll')]
@@ -75,6 +86,16 @@ public function get(int $pollId): JSONResponse {
7586
/**
7687
* get complete poll
7788
* @param int $pollId Poll id
89+
*
90+
* psalm-return JSONResponse<array{
91+
* poll: Poll,
92+
* options: array<int, Option>,
93+
* votes: array<int, Vote>,
94+
* comments: array<int, Comment>,
95+
* shares: array<int, Share>,
96+
* subscribed: Subscription|null
97+
* }>
98+
*
7899
*/
79100
#[NoAdminRequired]
80101
#[FrontpageRoute(verb: 'GET', url: '/poll/{pollId}')]
@@ -93,6 +114,9 @@ public function getFull(int $pollId): JSONResponse {
93114
* Add poll
94115
* @param string $title Poll title
95116
* @param string $type Poll type ('datePoll', 'textPoll')
117+
* @param string $votingVariant Voting variant (default: Poll::VARIANT_SIMPLE)
118+
*
119+
* psalm-return JSONResponse<array{poll: Poll}>
96120
*/
97121
#[NoAdminRequired]
98122
#[FrontpageRoute(verb: 'POST', url: '/poll/add')]
@@ -109,6 +133,8 @@ public function add(string $type, string $title, string $votingVariant = Poll::V
109133
* Update poll configuration
110134
* @param int $pollId Poll id
111135
* @param array $poll poll config
136+
*
137+
* psalm-return JSONResponse<array{poll: Poll}>
112138
*/
113139
#[NoAdminRequired]
114140
#[FrontpageRoute(verb: 'PUT', url: '/poll/{pollId}')]
@@ -238,4 +264,84 @@ public function changeOwner(int $pollId, string $targetUserId): JSONResponse {
238264
public function getParticipantsEmailAddresses(int $pollId): JSONResponse {
239265
return $this->response(fn () => $this->pollService->getParticipantsEmailAddresses($pollId));
240266
}
267+
268+
/**
269+
* Get list of pollgroups
270+
*
271+
* psalm-return JSONResponse<array{pollGroups: array<int, PollGroup>}>
272+
*/
273+
#[NoAdminRequired]
274+
#[FrontpageRoute(verb: 'GET', url: '/pollgroups')]
275+
public function listPollGroups(): JSONResponse {
276+
return $this->response(function () {
277+
return [
278+
'pollGroups' => $this->pollService->listPollGroups(),
279+
];
280+
});
281+
}
282+
283+
/**
284+
* Create a new pollgroup with its title and add a poll to it
285+
*
286+
* @param int $pollId Poll id to add to the new pollgroup
287+
* @param string $newPollGroupName Name of the new pollgroup
288+
*
289+
* psalm-return JSONResponse<array{pollGroup: PollGroup, poll: Poll}>
290+
*/
291+
#[NoAdminRequired]
292+
#[FrontpageRoute(verb: 'POST', url: '/pollgroup/new/poll/{pollId}')]
293+
public function addPollToNewPollGroup(int $pollId, string $newPollGroupName = ''): JSONResponse {
294+
return $this->response(fn () => [
295+
'pollGroup' => $this->pollService->addPollToPollGroup($pollId, newPollGroupName: $newPollGroupName),
296+
'poll' => $this->pollService->get($pollId),
297+
]);
298+
}
299+
300+
/**
301+
* Add poll to pollgroup
302+
* @param int $pollId Poll id
303+
* @param int $pollGroupId Poll group id
304+
*
305+
* psalm-return JSONResponse<array{pollGroup: PollGroup, poll: Poll}>
306+
*/
307+
#[NoAdminRequired]
308+
#[FrontpageRoute(verb: 'PUT', url: '/pollgroup/{pollGroupId}/poll/{pollId}')]
309+
public function addPollToPollGroup(int $pollId, int $pollGroupId): JSONResponse {
310+
return $this->response(fn () => [
311+
'pollGroup' => $this->pollService->addPollToPollGroup($pollId, $pollGroupId),
312+
'poll' => $this->pollService->get($pollId),
313+
]);
314+
}
315+
316+
/**
317+
* Update Pollgroup
318+
*/
319+
#[NoAdminRequired]
320+
#[FrontpageRoute(verb: 'PUT', url: '/pollgroup/{pollGroupId}/update')]
321+
public function updatePollGroup(
322+
int $pollGroupId,
323+
string $title,
324+
string $titleExt,
325+
string $description,
326+
): JSONResponse {
327+
return $this->response(fn () => [
328+
'pollGroup' => $this->pollService->updatePollGroup($pollGroupId, $title, $titleExt, $description),
329+
]);
330+
}
331+
/**
332+
* Remove poll from pollgroup
333+
* @param int $pollId Poll id
334+
* @param int $pollGroupId Poll group id
335+
*
336+
* psalm-return JSONResponse<array{pollGroup: PollGroup | null, poll: Poll}>
337+
*/
338+
#[NoAdminRequired]
339+
#[FrontpageRoute(verb: 'DELETE', url: '/pollgroup/{pollGroupId}/poll/{pollId}')]
340+
public function removePollFromPollGroup(int $pollId, int $pollGroupId): JSONResponse {
341+
return $this->response(fn () => [
342+
'pollGroup' => $this->pollService->removePollFromPollGroup($pollId, $pollGroupId),
343+
'poll' => $this->pollService->get($pollId),
344+
]);
345+
}
346+
241347
}

lib/Db/EntityWithUser.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace OCA\Polls\Db;
1010

11+
use Exception;
1112
use OCA\Polls\Helper\Container;
1213
use OCA\Polls\Model\User\Anon;
1314
use OCA\Polls\Model\UserBase;
@@ -101,7 +102,14 @@ public function getUser(): UserBase {
101102

102103
$userMapper = (Container::queryClass(UserMapper::class));
103104

104-
$user = $userMapper->getParticipant($this->getUserId(), $this->getPollId());
105+
try {
106+
$pollId = $this->getPollId();
107+
$user = $userMapper->getParticipant($this->getUserId(), $pollId);
108+
// Get user from userbase
109+
} catch (Exception $e) {
110+
// If pollId is not set, we assume that the user is not a participant of a poll
111+
$user = $userMapper->getUserFromUserBase($this->getUserId());
112+
}
105113
return $user;
106114
}
107115
}

lib/Db/Poll.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class Poll extends EntityWithUser implements JsonSerializable {
172172
protected ?string $groupShares = '';
173173
protected int $optionsCount = 0;
174174
protected int $proposalsCount = 0;
175+
protected ?string $pollGroups = '';
175176

176177
// subqueried columns
177178
protected int $currentUserOrphanedVotes = 0;
@@ -230,6 +231,7 @@ public function jsonSerialize(): array {
230231
'status' => $this->getStatusArray(),
231232
'currentUserStatus' => $this->getCurrentUserStatus(),
232233
'permissions' => $this->getPermissionsArray(),
234+
'pollGroups' => $this->getPollGroups(),
233235
];
234236
}
235237

@@ -437,6 +439,18 @@ private function getGroupShares(): array {
437439
return [];
438440
}
439441

442+
/**
443+
* @return int[]
444+
*
445+
* @psalm-return list<int>
446+
*/
447+
public function getPollGroups(): array {
448+
if (!$this->pollGroups) {
449+
return [];
450+
}
451+
return array_map('intval', explode(PollGroup::CONCAT_SEPARATOR, $this->pollGroups));
452+
}
453+
440454
private function getAccess(): string {
441455
if ($this->access === self::ACCESS_PUBLIC) {
442456
return self::ACCESS_OPEN;

0 commit comments

Comments
 (0)