summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2023-02-02 07:00:50 +0100
committerGitHub <noreply@github.com>2023-02-02 07:00:50 +0100
commit820839c5f0fb28613c4b9fb5c08e41a842f703ac (patch)
tree1984d7e4a0ee2dd533f3f39e2adcbd4b882d97a1
parenta37667a99ea2c9c69b630f3ca76fac84278d79e7 (diff)
parent54bc20ce6b0e36f328c5e11c6e58581acdd0b783 (diff)
Merge pull request #8660 from nextcloud/feature/8610/add-endpoint-to-return-all-participants
Add an endpoint to return all participants
-rw-r--r--appinfo/routes/routesRoomController.php2
-rw-r--r--docs/breakout-rooms.md18
-rw-r--r--docs/participant.md10
-rw-r--r--lib/Controller/RoomController.php33
-rw-r--r--lib/Participant.php4
-rw-r--r--lib/Service/ParticipantService.php44
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php36
-rw-r--r--tests/integration/features/conversation/breakout-rooms.feature12
8 files changed, 149 insertions, 10 deletions
diff --git a/appinfo/routes/routesRoomController.php b/appinfo/routes/routesRoomController.php
index 9bbe59a94..7542b8884 100644
--- a/appinfo/routes/routesRoomController.php
+++ b/appinfo/routes/routesRoomController.php
@@ -66,6 +66,8 @@ return [
])],
/** @see \OCA\Talk\Controller\RoomController::getParticipants() */
['name' => 'Room#getParticipants', 'url' => '/api/{apiVersion}/room/{token}/participants', 'verb' => 'GET', 'requirements' => $requirementsWithToken],
+ /** @see \OCA\Talk\Controller\RoomController::getBreakoutRoomParticipants() */
+ ['name' => 'Room#getBreakoutRoomParticipants', 'url' => '/api/{apiVersion}/room/{token}/breakout-rooms/participants', 'verb' => 'GET', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::addParticipantToRoom() */
['name' => 'Room#addParticipantToRoom', 'url' => '/api/{apiVersion}/room/{token}/participants', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::removeSelfFromRoom() */
diff --git a/docs/breakout-rooms.md b/docs/breakout-rooms.md
index 2e160f99c..c77a9d9be 100644
--- a/docs/breakout-rooms.md
+++ b/docs/breakout-rooms.md
@@ -36,7 +36,7 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Array of conversations (breakout rooms and parent)
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Remove breakout rooms
@@ -50,7 +50,7 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Parent conversation
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Start breakout rooms
@@ -65,7 +65,7 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Array of conversations (breakout rooms and parent)
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Stop breakout rooms
@@ -80,7 +80,7 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Array of conversations (breakout rooms and parent)
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Broadcast message to breakout rooms
@@ -102,7 +102,7 @@ Group and public conversations can be used to host breakout rooms.
+ `404 Not Found` When the conversation could not be found for the participant
+ `413 Payload Too Large` When the message was longer than the allowed limit of 32000 characters (check the `spreed => config => chat => max-length` capability for the limit)
- Data: Array of conversations (breakout rooms and parent)
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Reorganize attendees
@@ -124,7 +124,7 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Array of conversations (breakout rooms and parent)
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Request assistance
@@ -139,7 +139,7 @@ This endpoint allows participants to raise their hand (token is the breakout roo
+ `400 Bad Request` Error `room`: When the room is not a breakout room or breakout rooms are not started
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Breakout room
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## Reset request for assistance
@@ -152,7 +152,7 @@ This endpoint allows participants to raise their hand (token is the breakout roo
+ `400 Bad Request` Error `room`: When the room is not a breakout room or breakout rooms are not started
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Breakout room
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
## List all breakout rooms
@@ -181,4 +181,4 @@ This endpoint allows participants to raise their hand (token is the breakout roo
+ `400 Bad Request` Error `target`: When the target room is not breakout room of the parent
+ `404 Not Found` When the conversation could not be found for the participant
- Data: Target breakout room
- See array definition in `Get user´s conversations` (API v4)
+ See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4)
diff --git a/docs/participant.md b/docs/participant.md
index 0a1ffdd08..f3faccf04 100644
--- a/docs/participant.md
+++ b/docs/participant.md
@@ -41,6 +41,16 @@
| `status` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation |
| `statusIcon` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation |
| `statusMessage` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation |
+| `roomToken` | string | v4 | | Optional: Only available with `breakout-rooms-v1` capability |
+
+
+## Get list of participants in a conversation including its breakout rooms
+
+* Required capability: `breakout-rooms-v1`
+* Method: `GET`
+* Endpoint: `/room/{token}/breakout-rooms/participants`
+* Data: See Data in [Get list of participants in a conversations](participant.md#get-list-of-participants-in-a-conversation)
+* Response: See Response in [Get list of participants in a conversations](participant.md#get-list-of-participants-in-a-conversation)
## Add a participant to a conversation
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index 2f725cc00..0e44e69a4 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -699,9 +699,39 @@ class RoomController extends AEnvironmentAwareController {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
- $maxPingAge = $this->timeFactory->getTime() - Session::SESSION_TIMEOUT_KILL;
$participants = $this->participantService->getSessionsAndParticipantsForRoom($this->room);
+
+ return $this->formatParticipantList($participants, $includeStatus);
+ }
+
+ /**
+ * @PublicPage
+ * @RequireParticipant
+ * @RequireModeratorOrNoLobby
+ *
+ * @param bool $includeStatus
+ * @return DataResponse
+ */
+ public function getBreakoutRoomParticipants(bool $includeStatus = false): DataResponse {
+ if ($this->participant->getAttendee()->getParticipantType() === Participant::GUEST) {
+ return new DataResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ $breakoutRooms = $this->breakoutRoomService->getBreakoutRooms($this->room, $this->participant);
+ $breakoutRooms[] = $this->room;
+ $participants = $this->participantService->getSessionsAndParticipantsForRooms($breakoutRooms);
+
+ return $this->formatParticipantList($participants, $includeStatus);
+ }
+
+ /**
+ * @param Participant[] $participants
+ * @param bool $includeStatus
+ * @return DataResponse
+ */
+ protected function formatParticipantList(array $participants, bool $includeStatus): DataResponse {
$results = $headers = $statuses = [];
+ $maxPingAge = $this->timeFactory->getTime() - Session::SESSION_TIMEOUT_KILL;
if ($this->userId !== null
&& $includeStatus
@@ -747,6 +777,7 @@ class RoomController extends AEnvironmentAwareController {
}
$result = [
+ 'roomToken' => $participant->getRoom()->getToken(),
'inCall' => Participant::FLAG_DISCONNECTED,
'lastPing' => 0,
'sessionIds' => [],
diff --git a/lib/Participant.php b/lib/Participant.php
index 83e80f7f5..b11a9f74b 100644
--- a/lib/Participant.php
+++ b/lib/Participant.php
@@ -67,6 +67,10 @@ class Participant {
$this->session = $session;
}
+ public function getRoom(): Room {
+ return $this->room;
+ }
+
public function getAttendee(): Attendee {
return $this->attendee;
}
diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php
index 2941972c8..f03fad5e6 100644
--- a/lib/Service/ParticipantService.php
+++ b/lib/Service/ParticipantService.php
@@ -1267,6 +1267,34 @@ class ParticipantService {
}
/**
+ * Get all sessions and attendees without a session for the room
+ *
+ * This will return multiple items for the same attendee if the attendee
+ * has multiple sessions in the room.
+ *
+ * @param Room[] $rooms
+ * @return Participant[]
+ */
+ public function getSessionsAndParticipantsForRooms(array $rooms): array {
+ $roomIds = array_map(static fn (Room $room) => $room->getId(), $rooms);
+ $map = array_combine($roomIds, $rooms);
+
+ $query = $this->connection->getQueryBuilder();
+
+ $helper = new SelectHelper();
+ $helper->selectAttendeesTable($query);
+ $helper->selectSessionsTable($query);
+ $query->from('talk_attendees', 'a')
+ ->leftJoin(
+ 'a', 'talk_sessions', 's',
+ $query->expr()->eq('s.attendee_id', 'a.id')
+ )
+ ->where($query->expr()->in('a.room_id', $query->createNamedParameter($roomIds, IQueryBuilder::PARAM_INT_ARRAY)));
+
+ return $this->getParticipantsForRoomsFromQuery($query, $map);
+ }
+
+ /**
* @param Room $room
* @param int $maxAge
* @return Participant[]
@@ -1350,12 +1378,28 @@ class ParticipantService {
/**
* @param IQueryBuilder $query
+ * @param Room $room
* @return Participant[]
*/
protected function getParticipantsFromQuery(IQueryBuilder $query, Room $room): array {
+ return $this->getParticipantsForRoomsFromQuery($query, [$room->getId() => $room]);
+ }
+
+ /**
+ * @param IQueryBuilder $query
+ * @param Room[] $rooms Room ID => Room object
+ * @psalm-param array<int, Room> $rooms
+ * @return Participant[]
+ */
+ protected function getParticipantsForRoomsFromQuery(IQueryBuilder $query, array $rooms): array {
$participants = [];
$result = $query->executeQuery();
while ($row = $result->fetch()) {
+ $room = $rooms[(int) $row['room_id']] ?? null;
+ if ($room === null) {
+ continue;
+ }
+
$attendee = $this->attendeeMapper->createAttendeeFromRow($row);
if (isset($row['s_id'])) {
$session = $this->sessionMapper->createSessionFromRow($row);
diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php
index aa5729251..f763bdfda 100644
--- a/tests/integration/features/bootstrap/FeatureContext.php
+++ b/tests/integration/features/bootstrap/FeatureContext.php
@@ -580,11 +580,44 @@ class FeatureContext implements Context, SnippetAcceptingContext {
if ($formData instanceof TableNode) {
$attendees = $this->getDataFromResponse($this->response);
+ } else {
+ $attendees = [];
+ }
+ $this->assertAttendeeList($identifier, $formData, $attendees);
+ }
+
+ /**
+ * @Then /^user "([^"]*)" sees the following attendees in breakout rooms for room "([^"]*)" with (\d+) \((v4)\)$/
+ *
+ * @param string $user
+ * @param string $identifier
+ * @param int $statusCode
+ * @param string $apiVersion
+ * @param TableNode $formData
+ */
+ public function userSeesAttendeesInBreakoutRoomsForRoom(string $user, string $identifier, int $statusCode, string $apiVersion, TableNode $formData = null): void {
+ $this->setCurrentUser($user);
+ $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/breakout-rooms/participants');
+ $this->assertStatusCode($this->response, $statusCode);
+
+ if ($formData instanceof TableNode) {
+ $attendees = $this->getDataFromResponse($this->response);
+ } else {
+ $attendees = [];
+ }
+ $this->assertAttendeeList($identifier, $formData, $attendees);
+ }
+
+ protected function assertAttendeeList(string $identifier, ?TableNode $formData, array $attendees): void {
+ if ($formData instanceof TableNode) {
$expectedKeys = array_flip($formData->getRows()[0]);
$result = [];
foreach ($attendees as $attendee) {
$data = [];
+ if (isset($expectedKeys['roomToken'])) {
+ $data['roomToken'] = self::$tokenToIdentifier[$attendee['roomToken']];
+ }
if (isset($expectedKeys['actorType'])) {
$data['actorType'] = $attendee['actorType'];
}
@@ -676,6 +709,9 @@ class FeatureContext implements Context, SnippetAcceptingContext {
}
protected static function sortAttendees(array $a1, array $a2): int {
+ if (array_key_exists('roomToken', $a1) && array_key_exists('roomToken', $a2) && $a1['roomToken'] !== $a2['roomToken']) {
+ return $a1['roomToken'] <=> $a2['roomToken'];
+ }
if (array_key_exists('participantType', $a1) && array_key_exists('participantType', $a2) && $a1['participantType'] !== $a2['participantType']) {
return $a1['participantType'] <=> $a2['participantType'];
}
diff --git a/tests/integration/features/conversation/breakout-rooms.feature b/tests/integration/features/conversation/breakout-rooms.feature
index 279fe5e09..aedc91e6f 100644
--- a/tests/integration/features/conversation/breakout-rooms.feature
+++ b/tests/integration/features/conversation/breakout-rooms.feature
@@ -41,6 +41,18 @@ Feature: conversation/breakout-rooms
| type | name |
| 2 | class room |
| 2 | Room 3 |
+ And user "participant1" sees the following attendees in breakout rooms for room "class room" with 200 (v4)
+ | roomToken | actorType | actorId | participantType |
+ | class room | users | participant1 | 1 |
+ | class room | users | participant2 | 3 |
+ | class room | users | participant3 | 3 |
+ | class room | users | participant4 | 3 |
+ | Room 1 | users | participant1 | 1 |
+ | Room 1 | users | participant2 | 3 |
+ | Room 2 | users | participant1 | 1 |
+ | Room 2 | users | participant3 | 3 |
+ | Room 3 | users | participant1 | 1 |
+ | Room 3 | users | participant4 | 3 |
Scenario: Teacher creates automatic breakout rooms
Given user "participant1" creates room "class room" (v4)