Skip to content

Commit

Permalink
fixup! feat(polls): allow editing of draft polls
Browse files Browse the repository at this point in the history
  • Loading branch information
miaulalala committed Nov 27, 2024
1 parent aabbdc4 commit 8e473b0
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 56 deletions.
28 changes: 28 additions & 0 deletions docs/poll.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`

See [Poll data](#poll-data)

# Edit a draft poll in a conversation

* Federation capability: `federation-v1`
* Method: `POST`
* Endpoint: `/poll/{token}/draft/{pollId]}`
* Data:

| field | type | Description |
|--------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `question` | string | The question of the poll |
| `options` | string[] | Array of strings with the voting options |
| `resultMode` | int | The result and voting mode of the poll, `0` means participants can immediatelly see the result and who voted for which option. `1` means the result is hidden until the poll is closed and then only the summary is published. |
| `maxVotes` | int | Maximum amount of options a participant can vote for |

* Response:
- Status code:
+ `201 Created`
+ `400 Bad Request` When the room is a one-to-one conversation
+ `400 Bad Request` When the question or the options were too long or invalid (not strings)
+ `403 Forbidden` When the conversation is read-only
+ `403 Forbidden` When the actor does not have chat permissions
+ `404 Not Found` When the conversation could not be found for the participant
+ `412 Precondition Failed` When the lobby is active and the user is not a moderator

- Data:

See [Poll data](#poll-data)

## Get state or result of a poll

* Federation capability: `federation-v1`
Expand Down
32 changes: 20 additions & 12 deletions lib/Controller/PollController.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ public function createPoll(string $question, array $options, int $resultMode, in
*
* Required capability: `edit-draft-poll`
*
* @param int $pollId
* @param int $pollId The poll id
* @param string $question Question of the poll
* @param string[] $options Options of the poll
* @param int $resultMode
* @psalm-param list<string> $options
* @param 0|1 $resultMode Mode how the results will be shown
* @psalm-param Poll::MODE_* $resultMode Mode how the results will be shown
* @param int $maxVotes Number of maximum votes per voter
* @return DataResponse<Http::STATUS_OK, TalkPollDraft, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'draft'|'options'|'question'|'room'}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: string}, array{}>
* @return DataResponse<Http::STATUS_OK, TalkPollDraft, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'draft'|'options'|'question'|'room'}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: string}, array{}>
*
* 200: Draft modified successfully
* 400: Modifying poll is not possible
Expand All @@ -158,7 +160,6 @@ public function createPoll(string $question, array $options, int $resultMode, in
#[RequireReadWriteConversation]
public function updateDraftPoll(int $pollId, string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
if ($this->room->isFederatedConversation()) {
// TODO: add editing a draft to federation
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController::class);
return $proxy->updateDraftPoll($pollId, $this->room, $this->participant, $question, $options, $resultMode, $maxVotes, $draft);
Expand All @@ -169,10 +170,6 @@ public function updateDraftPoll(int $pollId, string $question, array $options, i
return new DataResponse(['error' => PollPropertyException::REASON_ROOM], Http::STATUS_BAD_REQUEST);
}

if (!$this->participant->hasModeratorPermissions()) {
return new DataResponse(['error' => PollPropertyException::REASON_DRAFT], Http::STATUS_BAD_REQUEST);
}

try {
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
} catch (DoesNotExistException $e) {
Expand All @@ -183,8 +180,21 @@ public function updateDraftPoll(int $pollId, string $question, array $options, i
return new DataResponse(['error' => PollPropertyException::REASON_POLL], Http::STATUS_BAD_REQUEST);
}

if (!$this->participant->hasModeratorPermissions()
&& ($poll->getActorType() !== $this->participant->getAttendee()->getActorType()
|| $poll->getActorId() !== $this->participant->getAttendee()->getActorId())) {
return new DataResponse(['error' => PollPropertyException::REASON_DRAFT], Http::STATUS_BAD_REQUEST);
}

try {
$question = $this->pollService->validatePollQuestion($question);
$encodedOptions = $this->pollService->validatePollOptions($options);
} catch (PollPropertyException $e) {
$this->logger->error('Error modifying poll', ['exception' => $e]);
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

$poll->setQuestion($question);
$encodedOptions = json_encode($options, JSON_THROW_ON_ERROR, 1);
$poll->setOptions($encodedOptions);
$poll->setResultMode($resultMode);
$poll->setMaxVotes($maxVotes);
Expand Down Expand Up @@ -373,10 +383,8 @@ public function closePoll(int $pollId): DataResponse {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

$poll->setStatus(Poll::STATUS_CLOSED);

try {
$this->pollService->updatePoll($this->participant, $poll);
$this->pollService->closePoll($this->participant, $poll);
} catch (WrongPermissionsException $e) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
} catch (Exception $e) {
Expand Down
116 changes: 76 additions & 40 deletions lib/Service/PollService.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,8 @@ public function __construct(
* @throws PollPropertyException
*/
public function createPoll(int $roomId, string $actorType, string $actorId, string $displayName, string $question, array $options, int $resultMode, int $maxVotes, bool $draft): Poll {
$question = trim($question);

if ($question === '' || strlen($question) > 32_000) {
throw new PollPropertyException(PollPropertyException::REASON_QUESTION);
}

try {
json_encode($options, JSON_THROW_ON_ERROR, 1);
} catch (\Exception) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

$validOptions = [];
foreach ($options as $option) {
if (!is_string($option)) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

$option = trim($option);
if ($option !== '') {
$validOptions[] = $option;
}
}

if (count($validOptions) < 2) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

try {
$jsonOptions = json_encode($validOptions, JSON_THROW_ON_ERROR, 1);
} catch (\Exception) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

if (strlen($jsonOptions) > 60_000) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}
$question = $this->validatePollQuestion($question);
$jsonOptions = $this->validatePollOptions($options);

$poll = new Poll();
$poll->setRoomId($roomId);
Expand Down Expand Up @@ -112,17 +77,76 @@ public function getPoll(int $roomId, int $pollId): Poll {
/**
* @param Participant $participant
* @param Poll $poll
* @throws WrongPermissionsException
* @throws Exception
* @throws WrongPermissionsException
*/
public function updatePoll(Participant $participant, Poll $poll): void {
if (!$participant->hasModeratorPermissions()
&& ($poll->getActorType() !== $participant->getAttendee()->getActorType()
|| $poll->getActorId() !== $participant->getAttendee()->getActorId())) {
&& ($poll->getActorType() !== $participant->getAttendee()->getActorType()
|| $poll->getActorId() !== $participant->getAttendee()->getActorId())) {
// Only moderators and the author of the poll can update it
throw new WrongPermissionsException();
}
$this->pollMapper->update($poll);
}

/**
* @param array $options
* @return string
*
* @since 21.0.0
*/
public function validatePollOptions(array $options): string {
try {
json_encode($options, JSON_THROW_ON_ERROR, 1);
} catch (\Exception) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

$validOptions = [];
foreach ($options as $option) {
if (!is_string($option)) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

$option = trim($option);
if ($option !== '') {
$validOptions[] = $option;
}
}

if (count($validOptions) < 2) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

try {
$jsonOptions = json_encode($validOptions, JSON_THROW_ON_ERROR, 1);
} catch (\Exception) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

if (strlen($jsonOptions) > 60_000) {
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
}

return $jsonOptions;
}

/**
* @param Participant $participant
* @param Poll $poll
* @return void
* @throws WrongPermissionsException
*/
public function closePoll(Participant $participant, Poll $poll): void {
if (!$participant->hasModeratorPermissions()
&& ($poll->getActorType() !== $participant->getAttendee()->getActorType()
|| $poll->getActorId() !== $participant->getAttendee()->getActorId())) {
// Only moderators and the author of the poll can update it
throw new WrongPermissionsException();
}

$poll->setStatus(Poll::STATUS_CLOSED);
$this->pollMapper->update($poll);
}

Expand Down Expand Up @@ -347,4 +371,16 @@ public function neutralizeDeletedUser(string $actorType, string $actorId): void
->andWhere($update->expr()->eq('actor_id', $update->createNamedParameter($actorId)));
$update->executeStatement();
}

/**
* @param string $question
* @return string
*/
public function validatePollQuestion(string $question): string {
$question = trim($question);
if ($question === '' || strlen($question) > 32_000) {
throw new PollPropertyException(PollPropertyException::REASON_QUESTION);
}
return $question;
}
}
15 changes: 13 additions & 2 deletions openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -9245,6 +9245,7 @@
"required": [
"question",
"options",
"resultMode",
"maxVotes"
],
"properties": {
Expand All @@ -9259,6 +9260,15 @@
"type": "string"
}
},
"resultMode": {
"type": "integer",
"format": "int64",
"enum": [
0,
1
],
"description": "Mode how the results will be shown"
},
"maxVotes": {
"type": "integer",
"format": "int64",
Expand Down Expand Up @@ -9294,10 +9304,11 @@
{
"name": "pollId",
"in": "path",
"description": "The poll id",
"required": true,
"schema": {
"type": "string",
"pattern": "^\\d+$"
"type": "integer",
"format": "int64"
}
},
{
Expand Down
15 changes: 13 additions & 2 deletions openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -9132,6 +9132,7 @@
"required": [
"question",
"options",
"resultMode",
"maxVotes"
],
"properties": {
Expand All @@ -9146,6 +9147,15 @@
"type": "string"
}
},
"resultMode": {
"type": "integer",
"format": "int64",
"enum": [
0,
1
],
"description": "Mode how the results will be shown"
},
"maxVotes": {
"type": "integer",
"format": "int64",
Expand Down Expand Up @@ -9181,10 +9191,11 @@
{
"name": "pollId",
"in": "path",
"description": "The poll id",
"required": true,
"schema": {
"type": "string",
"pattern": "^\\d+$"
"type": "integer",
"format": "int64"
}
},
{
Expand Down
Loading

0 comments on commit 8e473b0

Please sign in to comment.