mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-18 05:20:50 +01:00
feat(polls): Allow moderators to draft polls
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
81c8bfca58
commit
7adfeb2218
14 changed files with 736 additions and 26 deletions
|
|
@ -21,6 +21,8 @@ return [
|
|||
'ocs' => [
|
||||
/** @see \OCA\Talk\Controller\PollController::createPoll() */
|
||||
['name' => 'Poll#createPoll', 'url' => '/api/{apiVersion}/poll/{token}', 'verb' => 'POST', 'requirements' => $requirements],
|
||||
/** @see \OCA\Talk\Controller\PollController::getAllDraftPolls() */
|
||||
['name' => 'Poll#getAllDraftPolls', 'url' => '/api/{apiVersion}/poll/{token}/drafts', 'verb' => 'GET', 'requirements' => $requirements],
|
||||
/** @see \OCA\Talk\Controller\PollController::showPoll() */
|
||||
['name' => 'Poll#showPoll', 'url' => '/api/{apiVersion}/poll/{token}/{pollId}', 'verb' => 'GET', 'requirements' => $requirementsWithPollId],
|
||||
/** @see \OCA\Talk\Controller\PollController::votePoll() */
|
||||
|
|
|
|||
|
|
@ -158,4 +158,5 @@
|
|||
|
||||
## 20.1
|
||||
* `archived-conversations` (local) - Conversations can be marked as archived which will hide them from the conversation list by default
|
||||
* `talk-polls-drafts` - Whether moderators can store and retrieve poll drafts
|
||||
* `config => call => start-without-media` (local) - Boolean, whether media should be disabled when starting or joining a conversation
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class Capabilities implements IPublicCapability {
|
|||
'mention-permissions',
|
||||
'edit-messages-note-to-self',
|
||||
'archived-conversations',
|
||||
'talk-polls-drafts',
|
||||
];
|
||||
|
||||
public const LOCAL_FEATURES = [
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use OCA\Talk\Chat\ChatManager;
|
|||
use OCA\Talk\Exceptions\WrongPermissionsException;
|
||||
use OCA\Talk\Middleware\Attribute\FederationSupported;
|
||||
use OCA\Talk\Middleware\Attribute\RequireModeratorOrNoLobby;
|
||||
use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant;
|
||||
use OCA\Talk\Middleware\Attribute\RequireParticipant;
|
||||
use OCA\Talk\Middleware\Attribute\RequirePermission;
|
||||
use OCA\Talk\Middleware\Attribute\RequireReadWriteConversation;
|
||||
|
|
@ -58,6 +59,7 @@ class PollController extends AEnvironmentAwareController {
|
|||
* @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
|
||||
* @param bool $draft Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
|
||||
* @return DataResponse<Http::STATUS_CREATED, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array<empty>, array{}>
|
||||
*
|
||||
* 201: Poll created successfully
|
||||
|
|
@ -69,11 +71,11 @@ class PollController extends AEnvironmentAwareController {
|
|||
#[RequireParticipant]
|
||||
#[RequirePermission(permission: RequirePermission::CHAT)]
|
||||
#[RequireReadWriteConversation]
|
||||
public function createPoll(string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
|
||||
public function createPoll(string $question, array $options, int $resultMode, int $maxVotes, bool $draft = false): DataResponse {
|
||||
if ($this->room->isFederatedConversation()) {
|
||||
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
|
||||
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController::class);
|
||||
return $proxy->createPoll($this->room, $this->participant, $question, $options, $resultMode, $maxVotes);
|
||||
return $proxy->createPoll($this->room, $this->participant, $question, $options, $resultMode, $maxVotes, $draft);
|
||||
}
|
||||
|
||||
if ($this->room->getType() !== Room::TYPE_GROUP
|
||||
|
|
@ -81,6 +83,10 @@ class PollController extends AEnvironmentAwareController {
|
|||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
if ($draft === true && !$this->participant->hasModeratorPermissions()) {
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$attendee = $this->participant->getAttendee();
|
||||
try {
|
||||
$poll = $this->pollService->createPoll(
|
||||
|
|
@ -91,33 +97,66 @@ class PollController extends AEnvironmentAwareController {
|
|||
$question,
|
||||
$options,
|
||||
$resultMode,
|
||||
$maxVotes
|
||||
$maxVotes,
|
||||
$draft,
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Error creating poll', ['exception' => $e]);
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$message = json_encode([
|
||||
'message' => 'object_shared',
|
||||
'parameters' => [
|
||||
'objectType' => 'talk-poll',
|
||||
'objectId' => $poll->getId(),
|
||||
'metaData' => [
|
||||
'type' => 'talk-poll',
|
||||
'id' => $poll->getId(),
|
||||
'name' => $question,
|
||||
]
|
||||
],
|
||||
], JSON_THROW_ON_ERROR);
|
||||
if (!$draft) {
|
||||
$message = json_encode([
|
||||
'message' => 'object_shared',
|
||||
'parameters' => [
|
||||
'objectType' => 'talk-poll',
|
||||
'objectId' => $poll->getId(),
|
||||
'metaData' => [
|
||||
'type' => 'talk-poll',
|
||||
'id' => $poll->getId(),
|
||||
'name' => $question,
|
||||
]
|
||||
],
|
||||
], JSON_THROW_ON_ERROR);
|
||||
|
||||
try {
|
||||
$this->chatManager->addSystemMessage($this->room, $attendee->getActorType(), $attendee->getActorId(), $message, $this->timeFactory->getDateTime(), true);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
try {
|
||||
$this->chatManager->addSystemMessage($this->room, $attendee->getActorType(), $attendee->getActorId(), $message, $this->timeFactory->getDateTime(), true);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
|
||||
return new DataResponse($this->renderPoll($poll, []), Http::STATUS_CREATED);
|
||||
return new DataResponse($this->renderPoll($poll), Http::STATUS_CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all drafted polls
|
||||
*
|
||||
* Required capability: `talk-polls-drafts`
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkPoll>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
|
||||
*
|
||||
* 200: Poll returned
|
||||
* 403: User is not a moderator
|
||||
* 404: Poll not found
|
||||
*/
|
||||
#[FederationSupported]
|
||||
#[PublicPage]
|
||||
#[RequireModeratorParticipant]
|
||||
public function getAllDraftPolls(): DataResponse {
|
||||
if ($this->room->isFederatedConversation()) {
|
||||
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
|
||||
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController::class);
|
||||
return $proxy->getDraftsForRoom($this->room, $this->participant);
|
||||
}
|
||||
|
||||
$polls = $this->pollService->getDraftsForRoom($this->room->getId());
|
||||
$data = [];
|
||||
foreach ($polls as $poll) {
|
||||
$data[] = $this->renderPoll($poll);
|
||||
}
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +182,11 @@ class PollController extends AEnvironmentAwareController {
|
|||
|
||||
try {
|
||||
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
|
||||
} catch (DoesNotExistException $e) {
|
||||
} catch (DoesNotExistException) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($poll->getStatus() === Poll::STATUS_DRAFT && !$this->participant->hasModeratorPermissions()) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +224,11 @@ class PollController extends AEnvironmentAwareController {
|
|||
|
||||
try {
|
||||
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
|
||||
} catch (\Exception $e) {
|
||||
} catch (DoesNotExistException) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($poll->getStatus() === Poll::STATUS_DRAFT) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -222,9 +269,10 @@ class PollController extends AEnvironmentAwareController {
|
|||
*
|
||||
* @param int $pollId ID of the poll
|
||||
* @psalm-param non-negative-int $pollId
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_ACCEPTED|Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
|
||||
*
|
||||
* 200: Poll closed successfully
|
||||
* 202: Poll draft was deleted successfully
|
||||
* 400: Poll already closed
|
||||
* 403: Missing permissions to close poll
|
||||
* 404: Poll not found
|
||||
|
|
@ -242,10 +290,15 @@ class PollController extends AEnvironmentAwareController {
|
|||
|
||||
try {
|
||||
$poll = $this->pollService->getPoll($this->room->getId(), $pollId);
|
||||
} catch (\Exception $e) {
|
||||
} catch (DoesNotExistException) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($poll->getStatus() === Poll::STATUS_DRAFT) {
|
||||
$this->pollService->deleteByPollId($poll->getId());
|
||||
return new DataResponse([], Http::STATUS_ACCEPTED);
|
||||
}
|
||||
|
||||
if ($poll->getStatus() === Poll::STATUS_CLOSED) {
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,38 @@ class PollController {
|
|||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkPoll>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Polls returned
|
||||
* 404: Polls not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::showPoll()
|
||||
*/
|
||||
public function getDraftsForRoom(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/drafts',
|
||||
);
|
||||
|
||||
$status = $proxy->getStatusCode();
|
||||
if ($status === Http::STATUS_NOT_FOUND || $status === Http::STATUS_FORBIDDEN) {
|
||||
return new DataResponse([], $status);
|
||||
}
|
||||
|
||||
/** @var list<TalkPoll> $list */
|
||||
$list = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$data = [];
|
||||
foreach ($list as $poll) {
|
||||
$data[] = $this->userConverter->convertPoll($room, $poll);
|
||||
}
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
|
|
@ -101,7 +133,7 @@ class PollController {
|
|||
*
|
||||
* @see \OCA\Talk\Controller\PollController::createPoll()
|
||||
*/
|
||||
public function createPoll(Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
|
||||
public function createPoll(Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes, bool $draft): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
|
|
@ -111,6 +143,7 @@ class PollController {
|
|||
'options' => $options,
|
||||
'resultMode' => $resultMode,
|
||||
'maxVotes' => $maxVotes,
|
||||
'draft' => $draft,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ use OCP\AppFramework\Db\Entity;
|
|||
class Poll extends Entity {
|
||||
public const STATUS_OPEN = 0;
|
||||
public const STATUS_CLOSED = 1;
|
||||
public const STATUS_DRAFT = 2;
|
||||
public const MODE_PUBLIC = 0;
|
||||
public const MODE_HIDDEN = 1;
|
||||
public const MAX_VOTES_UNLIMITED = 0;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,20 @@ class PollMapper extends QBMapper {
|
|||
parent::__construct($db, 'talk_polls', Poll::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Poll[]
|
||||
*/
|
||||
public function getDraftsByRoomId(int $roomId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('status', $query->createNamedParameter(Poll::STATUS_DRAFT, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pollId
|
||||
* @return Poll
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class PollService {
|
|||
) {
|
||||
}
|
||||
|
||||
public function createPoll(int $roomId, string $actorType, string $actorId, string $displayName, string $question, array $options, int $resultMode, int $maxVotes): Poll {
|
||||
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) {
|
||||
|
|
@ -78,12 +78,23 @@ class PollService {
|
|||
$poll->setVotes(json_encode([]));
|
||||
$poll->setResultMode($resultMode);
|
||||
$poll->setMaxVotes($maxVotes);
|
||||
if ($draft) {
|
||||
$poll->setStatus(Poll::STATUS_DRAFT);
|
||||
}
|
||||
|
||||
$this->pollMapper->insert($poll);
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $roomId
|
||||
* @return Poll[]
|
||||
*/
|
||||
public function getDraftsForRoom(int $roomId): array {
|
||||
return $this->pollMapper->getDraftsByRoomId($roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $roomId
|
||||
* @param int $pollId
|
||||
|
|
|
|||
|
|
@ -8724,6 +8724,11 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Number of maximum votes per voter"
|
||||
},
|
||||
"draft": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8825,6 +8830,149 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
|
||||
"get": {
|
||||
"operationId": "poll-get-all-draft-polls",
|
||||
"summary": "Get all drafted polls",
|
||||
"description": "Required capability: `talk-polls-drafts`",
|
||||
"tags": [
|
||||
"poll"
|
||||
],
|
||||
"security": [
|
||||
{},
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiVersion",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v1"
|
||||
],
|
||||
"default": "v1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9]{4,30}$"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Poll returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Poll"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "User is not a moderator",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Poll not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
|
||||
"get": {
|
||||
"operationId": "poll-show-poll",
|
||||
|
|
@ -9205,6 +9353,34 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"202": {
|
||||
"description": "Poll draft was deleted successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Poll already closed",
|
||||
"content": {
|
||||
|
|
|
|||
176
openapi.json
176
openapi.json
|
|
@ -8611,6 +8611,11 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Number of maximum votes per voter"
|
||||
},
|
||||
"draft": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8712,6 +8717,149 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
|
||||
"get": {
|
||||
"operationId": "poll-get-all-draft-polls",
|
||||
"summary": "Get all drafted polls",
|
||||
"description": "Required capability: `talk-polls-drafts`",
|
||||
"tags": [
|
||||
"poll"
|
||||
],
|
||||
"security": [
|
||||
{},
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiVersion",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v1"
|
||||
],
|
||||
"default": "v1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9]{4,30}$"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Poll returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Poll"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "User is not a moderator",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Poll not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
|
||||
"get": {
|
||||
"operationId": "poll-show-poll",
|
||||
|
|
@ -9092,6 +9240,34 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"202": {
|
||||
"description": "Poll draft was deleted successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Poll already closed",
|
||||
"content": {
|
||||
|
|
|
|||
|
|
@ -583,6 +583,26 @@ export type paths = {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* Get all drafted polls
|
||||
* @description Required capability: `talk-polls-drafts`
|
||||
*/
|
||||
get: operations["poll-get-all-draft-polls"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -5112,6 +5132,11 @@ export interface operations {
|
|||
* @description Number of maximum votes per voter
|
||||
*/
|
||||
maxVotes: number;
|
||||
/**
|
||||
* @description Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
|
||||
* @default false
|
||||
*/
|
||||
draft?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -5146,6 +5171,65 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
"poll-get-all-draft-polls": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header: {
|
||||
/** @description Required to be true for the API request to pass */
|
||||
"OCS-APIRequest": boolean;
|
||||
};
|
||||
path: {
|
||||
apiVersion: "v1";
|
||||
token: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Poll returned */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: components["schemas"]["Poll"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description User is not a moderator */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
"poll-show-poll": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -5295,6 +5379,20 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll draft was deleted successfully */
|
||||
202: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll already closed */
|
||||
400: {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -583,6 +583,26 @@ export type paths = {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/drafts": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* Get all drafted polls
|
||||
* @description Required capability: `talk-polls-drafts`
|
||||
*/
|
||||
get: operations["poll-get-all-draft-polls"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/ocs/v2.php/apps/spreed/api/{apiVersion}/poll/{token}/{pollId}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -4593,6 +4613,11 @@ export interface operations {
|
|||
* @description Number of maximum votes per voter
|
||||
*/
|
||||
maxVotes: number;
|
||||
/**
|
||||
* @description Whether the poll should be saved as a draft (only allowed for moderators and with `talk-polls-drafts` capability)
|
||||
* @default false
|
||||
*/
|
||||
draft?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -4627,6 +4652,65 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
"poll-get-all-draft-polls": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header: {
|
||||
/** @description Required to be true for the API request to pass */
|
||||
"OCS-APIRequest": boolean;
|
||||
};
|
||||
path: {
|
||||
apiVersion: "v1";
|
||||
token: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Poll returned */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: components["schemas"]["Poll"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description User is not a moderator */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
"poll-show-poll": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -4776,6 +4860,20 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll draft was deleted successfully */
|
||||
202: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": {
|
||||
ocs: {
|
||||
meta: components["schemas"]["OCSMeta"];
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description Poll already closed */
|
||||
400: {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -2407,6 +2407,9 @@ class FeatureContext implements Context, SnippetAcceptingContext {
|
|||
if ($data['maxVotes'] === 'unlimited') {
|
||||
$data['maxVotes'] = 0;
|
||||
}
|
||||
if (isset($data['draft'])) {
|
||||
$data['draft'] = (bool)$data['draft'];
|
||||
}
|
||||
|
||||
$this->setCurrentUser($user);
|
||||
$this->sendRequest(
|
||||
|
|
@ -2528,6 +2531,8 @@ class FeatureContext implements Context, SnippetAcceptingContext {
|
|||
$expected['status'] = 0;
|
||||
} elseif ($expected['status'] === 'closed') {
|
||||
$expected['status'] = 1;
|
||||
} elseif ($expected['status'] === 'draft') {
|
||||
$expected['status'] = 2;
|
||||
}
|
||||
|
||||
if (str_ends_with($expected['actorId'], '@{$LOCAL_URL}')) {
|
||||
|
|
|
|||
|
|
@ -805,3 +805,44 @@ Feature: chat-2/poll
|
|||
Then user "participant1" sees the following system messages in room "room" with 200 (v1)
|
||||
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
|
||||
| room | users | participant1 | history_cleared | You cleared the history of the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
|
||||
|
||||
Scenario: Create a public poll without max votes limit
|
||||
Given user "participant1" creates room "room" (v4)
|
||||
| roomType | 2 |
|
||||
| roomName | room |
|
||||
When user "participant1" adds user "participant2" to room "room" with 200 (v4)
|
||||
When user "participant1" creates a poll in room "room" with 201
|
||||
| question | What is the question? |
|
||||
| options | ["Where are you?","How much is the fish?"] |
|
||||
| resultMode | public |
|
||||
| maxVotes | unlimited |
|
||||
| draft | 1 |
|
||||
Then user "participant1" sees the following messages in room "room" with 200
|
||||
| room | actorType | actorId | actorDisplayName | message | messageParameters |
|
||||
Then user "participant1" sees poll "What is the question?" in room "room" with 200
|
||||
| id | POLL_ID(What is the question?) |
|
||||
| question | What is the question? |
|
||||
| options | ["Where are you?","How much is the fish?"] |
|
||||
| votes | [] |
|
||||
| numVoters | 0 |
|
||||
| resultMode | public |
|
||||
| maxVotes | unlimited |
|
||||
| actorType | users |
|
||||
| actorId | participant1 |
|
||||
| actorDisplayName | participant1-displayname |
|
||||
| status | draft |
|
||||
| votedSelf | not voted |
|
||||
Then user "participant2" sees poll "What is the question?" in room "room" with 404
|
||||
Then user "participant1" votes for options "[1]" on poll "What is the question?" in room "room" with 404
|
||||
Then user "participant2" votes for options "[1]" on poll "What is the question?" in room "room" with 404
|
||||
Then user "participant1" closes poll "What is the question?" in room "room" with 202
|
||||
Then user "participant1" sees poll "What is the question?" in room "room" with 404
|
||||
Then user "participant2" sees poll "What is the question?" in room "room" with 404
|
||||
Then user "participant1" sees the following system messages in room "room" with 200 (v1)
|
||||
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
|
||||
| room | users | participant1 | user_added | You added {user} | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
|
||||
| room | users | participant1 | conversation_created | You created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
|
||||
Then user "participant2" sees the following system messages in room "room" with 200 (v1)
|
||||
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
|
||||
| room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
|
||||
| room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue