Fix issues mentioned in the polls review

* Question is limited to 32k chars (not multibyte save, so an emoji is 3-4 chars)
* Json encoded options array is limited to 60k chars (not multibyte save, so an emoji is 3-4 chars)
* Options on creation are now validated to be strings
* Options array on creating needs to be at least 2 strings long
* Options on voting are now validated to be integers
* System messages `poll_voted` do not contain the actor anymore to be okay with the hidden result mode

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2022-07-22 11:54:29 +02:00
Родитель c36cab3f55
Коммит 35fc94d947
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 74434EFE0D2E2205
4 изменённых файлов: 50 добавлений и 22 удалений

Просмотреть файл

@ -386,7 +386,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
* `reaction_deleted` - Reaction deleted by author (replacement of `reaction` after the action has been performed)
* `reaction_revoked` - {actor} deleted a reaction (the action that will replace `reaction` with a `reaction_deleted` message)
* Creating a poll is an `object_shared` with a poll object
* `poll_voted` - {actor} voted on the poll {poll}
* `poll_voted` - Someone voted on the poll {poll}
* `poll_closed` - {actor} closed the poll {poll}
* `message_expiration_enabled` - {actor} set the message expiration to 3 hours
* `message_expiration_disabled` - {actor} disabled message expiration

Просмотреть файл

@ -18,7 +18,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
* Response:
- Status code:
+ `201 Created`
+ `400 Bad Request` When the question or the options were too long
+ `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
@ -94,21 +94,21 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
It is therefor recommended to use `format=json` or send the `Content-Type: application/json` header,
to receive a JSON response.
| field | type | Description |
|--------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------|
| `id` | int | ID of the poll |
| `question` | string | The question of the poll |
| `options` | string[] | The options participants can vote for |
| `votes` | int[] | Map with `'option-' + optionId` => number of votes (only available for when the actor voted on public poll or the poll is closed) |
| `actorType` | string | Actor type of the poll author (see [Constants - Attendee types](constants.md#attendee-types)) |
| `actorId` | string | Actor ID identifying the poll author |
| `actorDisplayName` | string | Display name of the poll author |
| `status` | int | Status of the poll (see [Constants - Poll status](constants.md#poll-status)) |
| `resultMode` | int | Result mode of the poll (see [Constants - Poll mode](constants.md#poll-mode)) |
| `maxVotes` | int | Maximum amount of options a user can vote for, `0` means unlimited |
| `votedSelf` | int[] | Array of option ids the participant voted for |
| `numVoters` | int | The number of unique voters that (only available for when the actor voted on public poll or the poll is closed) |
| `details` | array[] | Detailed list who voted for which option (only available for public closed polls), see [Details](#details) below |
| field | type | Description |
|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | int | ID of the poll |
| `question` | string | The question of the poll |
| `options` | string[] | The options participants can vote for |
| `votes` | int[] | Map with `'option-' + optionId` => number of votes (only available for when the actor voted on public poll or the poll is closed) |
| `actorType` | string | Actor type of the poll author (see [Constants - Attendee types](constants.md#attendee-types)) |
| `actorId` | string | Actor ID identifying the poll author |
| `actorDisplayName` | string | Display name of the poll author |
| `status` | int | Status of the poll (see [Constants - Poll status](constants.md#poll-status)) |
| `resultMode` | int | Result mode of the poll (see [Constants - Poll mode](constants.md#poll-mode)) |
| `maxVotes` | int | Maximum amount of options a user can vote for, `0` means unlimited |
| `votedSelf` | int[] | Array of option ids the participant voted for |
| `numVoters` | int | The number of unique voters that voted (only available when the actor voted on public poll or the poll is closed unless for the creator and moderators) |
| `details` | array[] | Detailed list who voted for which option (only available for public closed polls), see [Details](#details) below |
### Details

Просмотреть файл

@ -497,10 +497,8 @@ class SystemMessage {
}
} elseif ($message === 'poll_voted') {
$parsedParameters['poll'] = $parameters['poll'];
$parsedMessage = $this->l->t('{actor} voted on the poll {poll}');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You voted on the poll {poll}');
}
$parsedMessage = $this->l->t('Someone voted on the poll {poll}');
unset($parsedParameters['actor']);
} else {
throw new \OutOfBoundsException('Unknown subject');
}

Просмотреть файл

@ -50,13 +50,37 @@ class PollService {
}
public function createPoll(int $roomId, string $actorType, string $actorId, string $displayName, string $question, array $options, int $resultMode, int $maxVotes): Poll {
if (strlen($question) > 32_000) {
throw new \UnexpectedValueException();
}
try {
$jsonOptions = json_encode($options, JSON_THROW_ON_ERROR, 1);
} catch (\Exception $e) {
throw new \RuntimeException();
}
foreach ($options as $option) {
if (!is_string($option)) {
throw new \RuntimeException();
}
}
if (count($options) < 2) {
throw new \RuntimeException();
}
if (strlen($jsonOptions) > 60_000) {
throw new \UnexpectedValueException();
}
$poll = new Poll();
$poll->setRoomId($roomId);
$poll->setActorType($actorType);
$poll->setActorId($actorId);
$poll->setDisplayName($displayName);
$poll->setQuestion($question);
$poll->setOptions(json_encode($options));
$poll->setOptions($jsonOptions);
$poll->setVotes(json_encode([]));
$poll->setResultMode($resultMode);
$poll->setMaxVotes($maxVotes);
@ -139,6 +163,12 @@ class PollService {
}
if (!empty($optionIds)) {
foreach ($optionIds as $optionId) {
if (!is_numeric($optionId)) {
throw new \RangeException();
}
}
$maxOptionId = max(array_keys(json_decode($poll->getOptions(), true, 512, JSON_THROW_ON_ERROR)));
$maxVotedId = max($optionIds);
$minVotedId = min($optionIds);