diff --git a/appinfo/info.xml b/appinfo/info.xml
index 2347e08c067..c705ca909eb 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]>
- 12.0.0-alpha.1
+ 12.0.0-alpha.2
agpl
Daniel Calviño Sánchez
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 83f76f137cd..13dc7f1f436 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -344,6 +344,15 @@
'token' => '^[a-z0-9]{4,30}$',
],
],
+ [
+ 'name' => 'Room#setAttendeePublishingPermissions',
+ 'url' => '/api/{apiVersion}/room/{token}/attendees/publishing-permissions',
+ 'verb' => 'PUT',
+ 'requirements' => [
+ 'apiVersion' => 'v(4)',
+ 'token' => '^[a-z0-9]{4,30}$',
+ ],
+ ],
[
'name' => 'Room#joinRoom',
'url' => '/api/{apiVersion}/room/{token}/participants/active',
diff --git a/docs/capabilities.md b/docs/capabilities.md
index ee36a552ad4..0e4765fcdaf 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -77,3 +77,4 @@ title: Capabilities
* `signaling-v3` - Whether signaling API v3 is available. This also means that v1 and v2 are **not** available anymore. The TURN and STUN server data is now returning all defined servers instead of a random one. Multiple entries for the same server are combined and using the urls array correctly now.
* `geo-location-sharing` - Whether the `geo-location` rich object is defined and can be shared to the rich-object sharing endpoint.
* `voice-message-sharing` - Shared files can be flagged as voice messages and are then presented differently in the interface
+* `publishing-permissions` - Whether the publishing permissions can be set for the attendees.
diff --git a/docs/constants.md b/docs/constants.md
index 62c3291b845..0946ee557b4 100644
--- a/docs/constants.md
+++ b/docs/constants.md
@@ -57,6 +57,12 @@ title: Constants
* `guests` - Guest without a login
* `emails` - A guest invited by email address
+### Attendee publishing permissions
+* `0` None
+* `1` Audio
+* `2` Video
+* `4` Screensharing
+
### Actor types of chat messages
* `users` - Logged-in users
* `guests` - Guest users (attendee type `guests` and `emails`)
diff --git a/docs/conversation.md b/docs/conversation.md
index d2d139d1ea3..4c52215cb68 100644
--- a/docs/conversation.md
+++ b/docs/conversation.md
@@ -37,6 +37,7 @@
`attendeePin` | string | v3 | | Unique dial-in authentication code for this user, when the conversation has SIP enabled (see `sipEnabled` attribute)
`actorType` | string | v3 | | Currently known `users|guests|emails|groups|circles`
`actorId` | string | v3 | | The unique identifier for the given actor type
+ `publishingPermissions` | int | v4 | Publishing permissions for the current participant (see [constants list](constants.md#attendee-publishing-permissions))
`participantInCall` | bool | v1 | v2 | **Removed:** use `participantFlags` instead
`participantFlags` | int | v1 | | "In call" flags of the user's session making the request (only available with `in-call-flags` capability)
`readOnly` | int | v1 | | Read-only state for the current user (only available with `read-only-rooms` capability)
diff --git a/docs/participant.md b/docs/participant.md
index 336f6bf4b60..c2dfab1b2df 100644
--- a/docs/participant.md
+++ b/docs/participant.md
@@ -34,6 +34,7 @@
`participantType` | int | v1 | | Permissions level of the participant (see [constants list](constants.md#participant-types))
`lastPing` | int | v1 | | Timestamp of the last ping of the user (should be used for sorting)
`inCall` | int | v1 | | Call flags the user joined with (see [constants list](constants.md#participant-in-call-flag))
+ `publishingPermissions` | int | v4 | Publishing permissions for the participant (see [constants list](constants.md#attendee-publishing-permissions))
`sessionId` | string | v1 | v4 | `'0'` if not connected, otherwise a 512 character long string
`sessionIds` | array | v4 | | array of session ids, each are 512 character long strings, or empty if no session
`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
@@ -190,6 +191,25 @@
+ `404 Not Found` When the conversation could not be found for the participant
+ `404 Not Found` When the participant to demote could not be found
+## Set publishing permissions for an attendee
+
+* Method: `PUT`
+* Endpoint: `/room/{token}/attendees/publishing-permissions`
+* Data:
+
+ field | type | Description
+ ---|---|---
+ `attendeeId` | int | Attendee id can be used for guests and users
+ `state` | int | New state for the attendee, see [constants list](constants.md#attendee-publishing-permissions)
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `400 Bad Request` When the conversation type does not support setting publishing permissions (only group and public conversations)
+ + `403 Forbidden` When the current user is not a moderator, owner or guest moderator
+ + `404 Not Found` When the conversation could not be found for the participant
+ + `404 Not Found` When the attendee to set publishing permissions could not be found
+
## Get a participant by their pin
Note: This is only allowed with validate SIP bridge requests
diff --git a/lib/Capabilities.php b/lib/Capabilities.php
index ee4190d9f56..5421e171205 100644
--- a/lib/Capabilities.php
+++ b/lib/Capabilities.php
@@ -93,6 +93,7 @@ public function getCapabilities(): array {
'geo-location-sharing',
'voice-message-sharing',
'signaling-v3',
+ 'publishing-permissions',
],
'config' => [
'attachments' => [
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index 8bed261aaa1..01eb2d4acb4 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -372,6 +372,7 @@ protected function formatRoomV4(Room $room, ?Participant $currentParticipant, bo
'actorType' => '',
'actorId' => '',
'attendeeId' => 0,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
'canEnableSIP' => false,
'attendeePin' => '',
'description' => '',
@@ -437,6 +438,7 @@ protected function formatRoomV4(Room $room, ?Participant $currentParticipant, bo
'actorType' => $attendee->getActorType(),
'actorId' => $attendee->getActorId(),
'attendeeId' => $attendee->getId(),
+ 'publishingPermissions' => $attendee->getPublishingPermissions(),
'description' => $room->getDescription(),
'listable' => $room->getListable(),
]);
@@ -906,6 +908,7 @@ public function getParticipants(bool $includeStatus = false): DataResponse {
'actorId' => $participant->getAttendee()->getActorId(),
'actorType' => $participant->getAttendee()->getActorType(),
'displayName' => $participant->getAttendee()->getActorId(),
+ 'publishingPermissions' => $participant->getAttendee()->getPublishingPermissions(),
'attendeePin' => '',
];
if ($this->talkConfig->isSIPConfigured()
@@ -1428,6 +1431,30 @@ protected function changeParticipantType(int $attendeeId, bool $promote): DataR
return new DataResponse();
}
+ /**
+ * @PublicPage
+ * @RequireModeratorParticipant
+ *
+ * @param int $attendeeId
+ * @param int $state
+ * @return DataResponse
+ */
+ public function setAttendeePublishingPermissions(int $attendeeId, int $state): DataResponse {
+ try {
+ $targetParticipant = $this->room->getParticipantByAttendeeId($attendeeId);
+ } catch (ParticipantNotFoundException $e) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ if ($this->room->getType() === Room::ONE_TO_ONE_CALL) {
+ return new DataResponse([], Http::STATUS_BAD_REQUEST);
+ }
+
+ $this->participantService->updatePublishingPermissions($this->room, $targetParticipant, $state);
+
+ return new DataResponse();
+ }
+
/**
* @NoAdminRequired
* @RequireModeratorParticipant
diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php
index 166e010f336..8b85b8f83f2 100644
--- a/lib/Controller/SignalingController.php
+++ b/lib/Controller/SignalingController.php
@@ -399,6 +399,7 @@ protected function getUsersInRoom(Room $room, int $pingTimestamp): array {
'lastPing' => $session->getLastPing(),
'sessionId' => $session->getSessionId(),
'inCall' => $session->getInCall(),
+ 'publishingPermissions' => $participant->getAttendee()->getPublishingPermissions(),
];
}
@@ -645,9 +646,17 @@ private function backendRoom(array $roomRequest): DataResponse {
}
}
- $permissions = ['publish-media', 'publish-screen'];
- if ($participant instanceof Participant && $participant->hasModeratorPermissions(false)) {
- $permissions[] = 'control';
+ $permissions = [];
+ if ($participant instanceof Participant) {
+ if ($participant->getAttendee()->getPublishingPermissions() & (Attendee::PUBLISHING_PERMISSIONS_AUDIO | Attendee::PUBLISHING_PERMISSIONS_VIDEO)) {
+ $permissions[] = 'publish-media';
+ }
+ if ($participant->getAttendee()->getPublishingPermissions() & Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING) {
+ $permissions[] = 'publish-screen';
+ }
+ if ($participant->hasModeratorPermissions(false)) {
+ $permissions[] = 'control';
+ }
}
$event = new SignalingEvent($room, $participant, $action);
diff --git a/lib/Migration/Version12000Date20210528100404.php b/lib/Migration/Version12000Date20210528100404.php
new file mode 100644
index 00000000000..e3f4a5802fc
--- /dev/null
+++ b/lib/Migration/Version12000Date20210528100404.php
@@ -0,0 +1,58 @@
+
+ *
+ * @author Daniel Calviño Sánchez
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\Talk\Migration;
+
+use Closure;
+use Doctrine\DBAL\Types\Types;
+use OCA\Talk\Model\Attendee;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version12000Date20210528100404 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('talk_attendees');
+ if (!$table->hasColumn('publishing_permissions')) {
+ $table->addColumn('publishing_permissions', Types::INTEGER, [
+ 'default' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
+
+ return $schema;
+ }
+
+ return null;
+ }
+}
diff --git a/lib/Model/Attendee.php b/lib/Model/Attendee.php
index 7766681cd39..5a758d4ae53 100644
--- a/lib/Model/Attendee.php
+++ b/lib/Model/Attendee.php
@@ -49,6 +49,8 @@
* @method int getLastMentionMessage()
* @method void setReadPrivacy(int $readPrivacy)
* @method int getReadPrivacy()
+ * @method void setPublishingPermissions(int $publishingPermissions)
+ * @method int getPublishingPermissions()
*/
class Attendee extends Entity {
public const ACTOR_USERS = 'users';
@@ -57,6 +59,12 @@ class Attendee extends Entity {
public const ACTOR_EMAILS = 'emails';
public const ACTOR_CIRCLES = 'circles';
+ public const PUBLISHING_PERMISSIONS_NONE = 0;
+ public const PUBLISHING_PERMISSIONS_AUDIO = 1;
+ public const PUBLISHING_PERMISSIONS_VIDEO = 2;
+ public const PUBLISHING_PERMISSIONS_SCREENSHARING = 4;
+ public const PUBLISHING_PERMISSIONS_ALL = 7;
+
/** @var int */
protected $roomId;
@@ -93,6 +101,9 @@ class Attendee extends Entity {
/** @var int */
protected $readPrivacy;
+ /** @var int */
+ protected $publishingPermissions;
+
public function __construct() {
$this->addType('roomId', 'int');
$this->addType('actorType', 'string');
@@ -106,6 +117,7 @@ public function __construct() {
$this->addType('lastReadMessage', 'int');
$this->addType('lastMentionMessage', 'int');
$this->addType('readPrivacy', 'int');
+ $this->addType('publishingPermissions', 'int');
}
public function getDisplayName(): string {
@@ -130,6 +142,7 @@ public function asArray(): array {
'last_read_message' => $this->getLastReadMessage(),
'last_mention_message' => $this->getLastMentionMessage(),
'read_privacy' => $this->getReadPrivacy(),
+ 'publishing_permissions' => $this->getPublishingPermissions(),
];
}
}
diff --git a/lib/Model/AttendeeMapper.php b/lib/Model/AttendeeMapper.php
index 1b928fc9310..3e8586389ee 100644
--- a/lib/Model/AttendeeMapper.php
+++ b/lib/Model/AttendeeMapper.php
@@ -152,6 +152,7 @@ public function createAttendeeFromRow(array $row): Attendee {
'last_read_message' => (int) $row['last_read_message'],
'last_mention_message' => (int) $row['last_mention_message'],
'read_privacy' => (int) $row['read_privacy'],
+ 'publishing_permissions' => (int) $row['publishing_permissions'],
]);
}
}
diff --git a/lib/Model/SelectHelper.php b/lib/Model/SelectHelper.php
index 3bf0463c819..dfcab988ad9 100644
--- a/lib/Model/SelectHelper.php
+++ b/lib/Model/SelectHelper.php
@@ -69,6 +69,7 @@ public function selectAttendeesTable(IQueryBuilder $query, string $alias = 'a'):
->addSelect($alias . 'last_read_message')
->addSelect($alias . 'last_mention_message')
->addSelect($alias . 'read_privacy')
+ ->addSelect($alias . 'publishing_permissions')
->selectAlias($alias . 'id', 'a_id');
}
diff --git a/lib/Room.php b/lib/Room.php
index 41b83f2ba58..42d99fd1d18 100644
--- a/lib/Room.php
+++ b/lib/Room.php
@@ -109,6 +109,8 @@ class Room {
public const EVENT_AFTER_USERS_ADD = self::class . '::postAddUsers';
public const EVENT_BEFORE_PARTICIPANT_TYPE_SET = self::class . '::preSetParticipantType';
public const EVENT_AFTER_PARTICIPANT_TYPE_SET = self::class . '::postSetParticipantType';
+ public const EVENT_BEFORE_PARTICIPANT_PUBLISHING_PERMISSIONS_SET = self::class . '::preSetParticipantPublishingPermissions';
+ public const EVENT_AFTER_PARTICIPANT_PUBLISHING_PERMISSIONS_SET = self::class . '::postSetParticipantPublishingPermissions';
public const EVENT_BEFORE_USER_REMOVE = self::class . '::preRemoveUser';
public const EVENT_AFTER_USER_REMOVE = self::class . '::postRemoveUser';
public const EVENT_BEFORE_PARTICIPANT_REMOVE = self::class . '::preRemoveBySession';
diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php
index a502af98d1a..2d1762dcc34 100644
--- a/lib/Service/ParticipantService.php
+++ b/lib/Service/ParticipantService.php
@@ -125,6 +125,25 @@ public function updateParticipantType(Room $room, Participant $participant, int
$this->dispatcher->dispatch(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, $event);
}
+ public function updatePublishingPermissions(Room $room, Participant $participant, int $newState): void {
+ $attendee = $participant->getAttendee();
+
+ if ($attendee->getActorType() === Attendee::ACTOR_GROUPS || $attendee->getActorType() === Attendee::ACTOR_CIRCLES) {
+ // Can not set publishing permissions for those actor types
+ return;
+ }
+
+ $oldState = $attendee->getPublishingPermissions();
+
+ $event = new ModifyParticipantEvent($room, $participant, 'publishingPermissions', $newState, $oldState);
+ $this->dispatcher->dispatch(Room::EVENT_BEFORE_PARTICIPANT_PUBLISHING_PERMISSIONS_SET, $event);
+
+ $attendee->setPublishingPermissions($newState);
+ $this->attendeeMapper->update($attendee);
+
+ $this->dispatcher->dispatch(Room::EVENT_AFTER_PARTICIPANT_PUBLISHING_PERMISSIONS_SET, $event);
+ }
+
public function updateLastReadMessage(Participant $participant, int $lastReadMessage): void {
$attendee = $participant->getAttendee();
$attendee->setLastReadMessage($lastReadMessage);
@@ -655,6 +674,14 @@ public function changeInCall(Room $room, Participant $participant, int $flags):
return;
}
+ $publishingPermissions = $participant->getAttendee()->getPublishingPermissions();
+ if (!($publishingPermissions & Attendee::PUBLISHING_PERMISSIONS_AUDIO)) {
+ $flags &= ~Participant::FLAG_WITH_AUDIO;
+ }
+ if (!($publishingPermissions & Attendee::PUBLISHING_PERMISSIONS_VIDEO)) {
+ $flags &= ~Participant::FLAG_WITH_VIDEO;
+ }
+
$event = new ModifyParticipantEvent($room, $participant, 'inCall', $flags, $session->getInCall());
if ($flags !== Participant::FLAG_DISCONNECTED) {
$this->dispatcher->dispatch(Room::EVENT_BEFORE_SESSION_JOIN_CALL, $event);
@@ -692,6 +719,14 @@ public function updateCallFlags(Room $room, Participant $participant, int $flags
throw new \InvalidArgumentException('Invalid flags');
}
+ $publishingPermissions = $participant->getAttendee()->getPublishingPermissions();
+ if (!($publishingPermissions & Attendee::PUBLISHING_PERMISSIONS_AUDIO)) {
+ $flags &= ~Participant::FLAG_WITH_AUDIO;
+ }
+ if (!($publishingPermissions & Attendee::PUBLISHING_PERMISSIONS_VIDEO)) {
+ $flags &= ~Participant::FLAG_WITH_VIDEO;
+ }
+
$event = new ModifyParticipantEvent($room, $participant, 'inCall', $flags, $session->getInCall());
$this->dispatcher->dispatch(Room::EVENT_BEFORE_SESSION_UPDATE_CALL_FLAGS, $event);
diff --git a/lib/Signaling/BackendNotifier.php b/lib/Signaling/BackendNotifier.php
index 50f906c87a2..2338f6b0d66 100644
--- a/lib/Signaling/BackendNotifier.php
+++ b/lib/Signaling/BackendNotifier.php
@@ -264,6 +264,7 @@ public function participantsModified(Room $room, array $sessionIds): void {
'lastPing' => 0,
'sessionId' => '0',
'participantType' => $attendee->getParticipantType(),
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
];
if ($attendee->getActorType() === Attendee::ACTOR_USERS) {
$data['userId'] = $attendee->getActorId();
@@ -274,10 +275,17 @@ public function participantsModified(Room $room, array $sessionIds): void {
$data['inCall'] = $session->getInCall();
$data['lastPing'] = $session->getLastPing();
$data['sessionId'] = $session->getSessionId();
+ $data['publishingPermissions'] = $attendee->getPublishingPermissions();
$users[] = $data;
if (\in_array($session->getSessionId(), $sessionIds, true)) {
- $data['permissions'] = ['publish-media', 'publish-screen'];
+ $data['permissions'] = [];
+ if ($attendee->getPublishingPermissions() & (Attendee::PUBLISHING_PERMISSIONS_AUDIO | Attendee::PUBLISHING_PERMISSIONS_VIDEO)) {
+ $data['permissions'][] = 'publish-media';
+ }
+ if ($attendee->getPublishingPermissions() & Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING) {
+ $data['permissions'][] = 'publish-screen';
+ }
if ($attendee->getParticipantType() === Participant::OWNER ||
$attendee->getParticipantType() === Participant::MODERATOR) {
$data['permissions'][] = 'control';
diff --git a/lib/Signaling/Listener.php b/lib/Signaling/Listener.php
index 9ebec18bdc0..59188abc09d 100644
--- a/lib/Signaling/Listener.php
+++ b/lib/Signaling/Listener.php
@@ -83,6 +83,7 @@ protected static function registerInternalSignaling(IEventDispatcher $dispatcher
$dispatcher->addListener(Room::EVENT_BEFORE_USER_REMOVE, $listener);
$dispatcher->addListener(Room::EVENT_BEFORE_PARTICIPANT_REMOVE, $listener);
$dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DISCONNECT, $listener);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PUBLISHING_PERMISSIONS_SET, $listener);
$listener = static function (RoomEvent $event) {
$room = $event->getRoom();
@@ -130,7 +131,8 @@ protected static function registerExternalSignaling(IEventDispatcher $dispatcher
// "participantsModified" once the clients no longer expect a
// "roomModified" message for participant type changes.
$dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, static function (ModifyParticipantEvent $event) {
+
+ $listener = static function (ModifyParticipantEvent $event) {
if (self::isUsingInternalSignaling()) {
return;
}
@@ -151,7 +153,10 @@ protected static function registerExternalSignaling(IEventDispatcher $dispatcher
}
$notifier->participantsModified($event->getRoom(), $sessionIds);
- });
+ };
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, $listener);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PUBLISHING_PERMISSIONS_SET, $listener);
+
$dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DELETE, static function (RoomEvent $event) {
if (self::isUsingInternalSignaling()) {
return;
diff --git a/src/PublicShareAuthSidebar.vue b/src/PublicShareAuthSidebar.vue
index 33f4d588cf3..1db9a79f6aa 100644
--- a/src/PublicShareAuthSidebar.vue
+++ b/src/PublicShareAuthSidebar.vue
@@ -40,7 +40,6 @@ import { getCurrentUser } from '@nextcloud/auth'
import { loadState } from '@nextcloud/initial-state'
import CallView from './components/CallView/CallView'
import ChatView from './components/ChatView'
-import { PARTICIPANT } from './constants'
import { EventBus } from './services/EventBus'
import {
leaveConversationSync,
@@ -153,7 +152,6 @@ export default {
await this.$store.dispatch('joinCall', {
token: this.token,
participantIdentifier: this.$store.getters.getParticipantIdentifier(),
- flags: PARTICIPANT.CALL_FLAG.IN_CALL,
})
},
diff --git a/src/components/CallView/CallView.vue b/src/components/CallView/CallView.vue
index c289a2129c0..5f470f3dbf1 100644
--- a/src/components/CallView/CallView.vue
+++ b/src/components/CallView/CallView.vue
@@ -30,6 +30,7 @@
{
},
})
- joinCall.mockResolvedValue()
+ // The requested flags and the actual flags can be different if some
+ // media device is not available.
+ const actualFlags = PARTICIPANT.CALL_FLAG.WITH_AUDIO
+ joinCall.mockResolvedValue(actualFlags)
expect(store.getters.isInCall(TOKEN)).toBe(false)
expect(store.getters.isConnecting(TOKEN)).toBe(false)
- const flags = PARTICIPANT.CALL_FLAG.WITH_VIDEO
+ const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
await store.dispatch('joinCall', {
token: TOKEN,
participantIdentifier: {
@@ -356,7 +359,7 @@ describe('participantsStore', () => {
{
attendeeId: 1,
sessionId: 'session-id-1',
- inCall: flags,
+ inCall: actualFlags,
participantType: PARTICIPANT.TYPE.USER,
},
])
@@ -381,12 +384,15 @@ describe('participantsStore', () => {
},
})
- joinCall.mockResolvedValue()
+ // The requested flags and the actual flags can be different if some
+ // media device is not available.
+ const actualFlags = PARTICIPANT.CALL_FLAG.WITH_AUDIO
+ joinCall.mockResolvedValue(actualFlags)
expect(store.getters.isInCall(TOKEN)).toBe(false)
expect(store.getters.isConnecting(TOKEN)).toBe(false)
- const flags = PARTICIPANT.CALL_FLAG.WITH_VIDEO
+ const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
await store.dispatch('joinCall', {
token: TOKEN,
participantIdentifier: {
@@ -402,7 +408,7 @@ describe('participantsStore', () => {
{
attendeeId: 1,
sessionId: 'session-id-1',
- inCall: flags,
+ inCall: actualFlags,
participantType: PARTICIPANT.TYPE.USER,
},
])
diff --git a/src/utils/webrtc/index.js b/src/utils/webrtc/index.js
index 73d2815887b..f2591455f84 100644
--- a/src/utils/webrtc/index.js
+++ b/src/utils/webrtc/index.js
@@ -116,7 +116,7 @@ function startCall(signaling, configuration) {
}
signaling.joinCall(pendingJoinCallToken, flags).then(() => {
- startedCall()
+ startedCall(flags)
}).catch(error => {
failedToStartCall(error)
})
@@ -127,18 +127,9 @@ function setupWebRtc() {
return
}
- const _signaling = signaling
-
- webRtc = initWebRtc(_signaling, callParticipantCollection, localCallParticipantModel)
+ webRtc = initWebRtc(signaling, callParticipantCollection, localCallParticipantModel)
localCallParticipantModel.setWebRtc(webRtc)
localMediaModel.setWebRtc(webRtc)
-
- webRtc.on('localMediaStarted', (configuration) => {
- startCall(_signaling, configuration)
- })
- webRtc.on('localMediaError', () => {
- startCall(_signaling, null)
- })
}
/**
@@ -159,9 +150,11 @@ async function signalingJoinConversation(token, sessionId) {
* Join the call of the given conversation
*
* @param {string} token Conversation to join the call
- * @returns {Promise}
+ * @param {int} flags Bitwise combination of PARTICIPANT.CALL_FLAG
+ * @returns {Promise} Resolved with the actual flags based on the
+ * available media
*/
-async function signalingJoinCall(token) {
+async function signalingJoinCall(token, flags) {
if (tokensInSignaling[token]) {
pendingJoinCallToken = token
@@ -175,11 +168,47 @@ async function signalingJoinCall(token) {
callAnalyzer = new CallAnalyzer(localMediaModel, null, callParticipantCollection)
}
+ const _signaling = signaling
+
return new Promise((resolve, reject) => {
startedCall = resolve
failedToStartCall = reject
- webRtc.startMedia(token)
+ // The previous state might be wiped after the media is started, so
+ // it should be saved now.
+ const enableAudio = !localStorage.getItem('audioDisabled_' + token)
+ const enableVideo = !localStorage.getItem('videoDisabled_' + token)
+
+ const startCallOnceLocalMediaStarted = (configuration) => {
+ webRtc.off('localMediaStarted', startCallOnceLocalMediaStarted)
+ webRtc.off('localMediaError', startCallOnceLocalMediaError)
+
+ if (enableAudio) {
+ localMediaModel.enableAudio()
+ } else {
+ localMediaModel.disableAudio()
+ }
+ if (enableVideo) {
+ localMediaModel.enableVideo()
+ } else {
+ localMediaModel.disableVideo()
+ }
+
+ startCall(_signaling, configuration)
+ }
+ const startCallOnceLocalMediaError = () => {
+ webRtc.off('localMediaStarted', startCallOnceLocalMediaStarted)
+ webRtc.off('localMediaError', startCallOnceLocalMediaError)
+
+ startCall(_signaling, null)
+ }
+
+ // ".once" can not be used, as both handlers need to be removed when
+ // just one of them is executed.
+ webRtc.on('localMediaStarted', startCallOnceLocalMediaStarted)
+ webRtc.on('localMediaError', startCallOnceLocalMediaError)
+
+ webRtc.startMedia(token, flags)
})
}
}
diff --git a/src/utils/webrtc/models/LocalMediaModel.js b/src/utils/webrtc/models/LocalMediaModel.js
index b17b579a2e4..6fcb535d4ae 100644
--- a/src/utils/webrtc/models/LocalMediaModel.js
+++ b/src/utils/webrtc/models/LocalMediaModel.js
@@ -206,7 +206,7 @@ LocalMediaModel.prototype = {
_setInitialMediaState: function(configuration) {
if (configuration.audio !== false) {
this.set('audioAvailable', true)
- if (!localStorage.getItem('audioDisabled_' + this.get('token')) || this.get('audioEnabled')) {
+ if (this.get('audioEnabled')) {
this.enableAudio()
} else {
this.disableAudio()
@@ -218,7 +218,7 @@ LocalMediaModel.prototype = {
if (configuration.video !== false) {
this.set('videoAvailable', true)
- if (!localStorage.getItem('videoDisabled_' + this.get('token')) || this.get('videoEnabled')) {
+ if (this.get('videoEnabled')) {
this.enableVideo()
} else {
this.disableVideo()
@@ -274,6 +274,11 @@ LocalMediaModel.prototype = {
}
this.set('localStream', null)
+
+ this.set('audioEnabled', false)
+ this.set('audioAvailable', false)
+ this.set('videoEnabled', false)
+ this.set('videoAvailable', false)
},
_handleAudioOn: function() {
diff --git a/src/utils/webrtc/simplewebrtc/localmedia.js b/src/utils/webrtc/simplewebrtc/localmedia.js
index bcb69904db8..ea678b1cf49 100644
--- a/src/utils/webrtc/simplewebrtc/localmedia.js
+++ b/src/utils/webrtc/simplewebrtc/localmedia.js
@@ -31,10 +31,6 @@ function LocalMedia(opts) {
const config = this.config = {
detectSpeakingEvents: false,
audioFallback: false,
- media: {
- audio: true,
- video: true,
- },
harkOptions: null,
logger: mockconsole,
}
@@ -53,6 +49,8 @@ function LocalMedia(opts) {
this._audioEnabled = true
this._videoEnabled = true
+ this._localMediaActive = false
+
this.localStreams = []
this._audioMonitorStreams = []
this.localScreens = []
@@ -121,9 +119,36 @@ const cloneLinkedStream = function(stream) {
return linkedStream
}
+/**
+ * Returns whether the local media is active or not.
+ *
+ * The local media is active if it has been started and not stopped yet, even if
+ * no media was available when started. An active local media will automatically
+ * react to changes in the selected media devices.
+ *
+ * @returns {bool} true if the local media is active, false otherwise
+ */
+LocalMedia.prototype.isLocalMediaActive = function() {
+ return this._localMediaActive
+}
+
LocalMedia.prototype.start = function(mediaConstraints, cb, context) {
const self = this
- const constraints = mediaConstraints || this.config.media
+ const constraints = mediaConstraints || { audio: true, video: true }
+
+ // If local media is started with neither audio nor video the local media
+ // will not be active (it will not react to changes in the selected media
+ // devices). It is just a special case in which starting succeeds with a null
+ // stream.
+ if (!constraints.audio && !constraints.video) {
+ self.emit('localStream', constraints, null)
+
+ if (cb) {
+ return cb(null, null, constraints)
+ }
+
+ return
+ }
if (!webrtcIndex.mediaDevicesManager.isSupported()) {
const error = new Error('MediaStreamError')
@@ -186,8 +211,10 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) {
webrtcIndex.mediaDevicesManager.on('change:audioInputId', self._handleAudioInputIdChangedBound)
webrtcIndex.mediaDevicesManager.on('change:videoInputId', self._handleVideoInputIdChangedBound)
+ self._localMediaActive = true
+
if (cb) {
- return cb(null, stream)
+ return cb(null, stream, constraints)
}
}).catch(function(err) {
// Fallback for users without a camera or with a camera that can not be
@@ -204,6 +231,8 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) {
webrtcIndex.mediaDevicesManager.on('change:audioInputId', self._handleAudioInputIdChangedBound)
webrtcIndex.mediaDevicesManager.on('change:videoInputId', self._handleVideoInputIdChangedBound)
+ self._localMediaActive = true
+
if (cb) {
return cb(err, null)
}
@@ -493,6 +522,8 @@ LocalMedia.prototype.stop = function(stream) {
webrtcIndex.mediaDevicesManager.off('change:audioInputId', this._handleAudioInputIdChangedBound)
webrtcIndex.mediaDevicesManager.off('change:videoInputId', this._handleVideoInputIdChangedBound)
+
+ this._localMediaActive = false
}
LocalMedia.prototype.stopStream = function(stream) {
diff --git a/src/utils/webrtc/simplewebrtc/simplewebrtc.js b/src/utils/webrtc/simplewebrtc/simplewebrtc.js
index ab919828e5e..86d37c62a0e 100644
--- a/src/utils/webrtc/simplewebrtc/simplewebrtc.js
+++ b/src/utils/webrtc/simplewebrtc/simplewebrtc.js
@@ -20,10 +20,6 @@ function SimpleWebRTC(opts) {
autoRemoveVideos: true,
adjustPeerVolume: false,
peerVolumeWhenSpeaking: 0.25,
- media: {
- video: true,
- audio: true,
- },
receiveMedia: {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
@@ -329,9 +325,9 @@ SimpleWebRTC.prototype.setVolumeForAll = function(volume) {
})
}
-SimpleWebRTC.prototype.joinCall = function(name) {
+SimpleWebRTC.prototype.joinCall = function(name, mediaConstraints) {
if (this.config.autoRequestMedia) {
- this.startLocalVideo()
+ this.startLocalVideo(mediaConstraints)
}
this.roomName = name
this.emit('joinedRoom', name)
@@ -345,17 +341,13 @@ SimpleWebRTC.prototype.getEl = function(idOrEl) {
}
}
-SimpleWebRTC.prototype.startLocalVideo = function() {
+SimpleWebRTC.prototype.startLocalVideo = function(mediaConstraints) {
const self = this
- const constraints = {
- audio: true,
- video: true,
- }
- this.webrtc.start(constraints, function(err, stream) {
+ this.webrtc.start(mediaConstraints, function(err, stream, actualConstraints) {
if (err) {
self.emit('localMediaError', err)
} else {
- self.emit('localMediaStarted', constraints)
+ self.emit('localMediaStarted', actualConstraints)
const localVideoContainer = self.getLocalVideoContainer()
if (localVideoContainer) {
diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js
index 98e47ea9636..39dc7e65bc1 100644
--- a/src/utils/webrtc/webrtc.js
+++ b/src/utils/webrtc/webrtc.js
@@ -525,10 +525,6 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
remoteVideosEl: '',
autoRequestMedia: true,
debug: false,
- media: {
- audio: true,
- video: true,
- },
autoAdjustMic: false,
audioFallback: true,
detectSpeakingEvents: true,
@@ -552,10 +548,17 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
webrtc.leaveCall()
})
- webrtc.startMedia = function(token) {
+ webrtc.startMedia = function(token, flags) {
startedWithMedia = undefined
- webrtc.joinCall(token)
+ // If no flags are provided try to enable both audio and video.
+ // Otherwise, try to enable only that allowed by the flags.
+ const mediaConstraints = {
+ audio: !flags || !!(flags & PARTICIPANT.CALL_FLAG.WITH_AUDIO),
+ video: !flags || !!(flags & PARTICIPANT.CALL_FLAG.WITH_VIDEO),
+ }
+
+ webrtc.joinCall(token, mediaConstraints)
}
const sendDataChannelToAll = function(channel, message, payload) {
@@ -745,6 +748,91 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
})
}
+ const reconnectOnPublishingPermissionsChange = (users) => {
+ const currentParticipant = users.find(user => {
+ const sessionId = user.sessionId || user.sessionid
+ return sessionId === signaling.getSessionId()
+ })
+
+ if (!currentParticipant) {
+ return
+ }
+
+ if (!currentParticipant.inCall) {
+ return
+ }
+
+ if (currentParticipant.publishingPermissions === undefined) {
+ return
+ }
+
+ if (currentParticipant.publishingPermissions === PARTICIPANT.PUBLISHING_PERMISSIONS.ALL && webrtc.webrtc.isLocalMediaActive()) {
+ return
+ }
+
+ if (currentParticipant.publishingPermissions === PARTICIPANT.PUBLISHING_PERMISSIONS.NONE && !webrtc.webrtc.isLocalMediaActive()) {
+ return
+ }
+
+ if (currentParticipant.publishingPermissions === PARTICIPANT.PUBLISHING_PERMISSIONS.NONE) {
+ startedWithMedia = undefined
+
+ webrtc.stopLocalVideo()
+
+ // If the MCU is used and there is no sending peer there is no need
+ // to force a reconnection, as there will be no connection that
+ // needs to be stopped.
+ if (!signaling.hasFeature('mcu') || ownPeer) {
+ forceReconnect(signaling, PARTICIPANT.CALL_FLAG.IN_CALL)
+ }
+
+ return
+ }
+
+ const forceReconnectOnceLocalMediaStarted = (constraints) => {
+ webrtc.off('localMediaStarted', forceReconnectOnceLocalMediaStarted)
+ webrtc.off('localMediaError', forceReconnectOnceLocalMediaError)
+
+ startedWithMedia = true
+
+ let flags = PARTICIPANT.CALL_FLAG.IN_CALL
+ if (constraints) {
+ if (constraints.audio) {
+ flags |= PARTICIPANT.CALL_FLAG.WITH_AUDIO
+ }
+ if (constraints.video && signaling.getSendVideoIfAvailable()) {
+ flags |= PARTICIPANT.CALL_FLAG.WITH_VIDEO
+ }
+ }
+
+ forceReconnect(signaling, flags)
+ }
+ const forceReconnectOnceLocalMediaError = () => {
+ webrtc.off('localMediaStarted', forceReconnectOnceLocalMediaStarted)
+ webrtc.off('localMediaError', forceReconnectOnceLocalMediaError)
+
+ startedWithMedia = false
+
+ // If the media fails to start there will be no media, so no need to
+ // reconnect. A reconnection will happen once the user selects a
+ // different device.
+ }
+
+ webrtc.on('localMediaStarted', forceReconnectOnceLocalMediaStarted)
+ webrtc.on('localMediaError', forceReconnectOnceLocalMediaError)
+
+ startedWithMedia = undefined
+
+ webrtc.startLocalVideo()
+ }
+
+ signaling.on('usersInRoom', function(users) {
+ reconnectOnPublishingPermissionsChange(users)
+ })
+ signaling.on('usersChanged', function(users) {
+ reconnectOnPublishingPermissionsChange(users)
+ })
+
webrtc.on('createdPeer', function(peer) {
console.debug('Peer created', peer)
diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php
index 57fa6a9d133..e3acd1ec2de 100644
--- a/tests/integration/features/bootstrap/FeatureContext.php
+++ b/tests/integration/features/bootstrap/FeatureContext.php
@@ -392,6 +392,9 @@ public function userSeesAttendeesInRoom(string $user, string $identifier, int $s
if (isset($expectedKeys['attendeePin'])) {
$data['attendeePin'] = $attendee['attendeePin'] ? '**PIN**' : '';
}
+ if (isset($expectedKeys['publishingPermissions'])) {
+ $data['publishingPermissions'] = (string) $attendee['publishingPermissions'];
+ }
if (!isset(self::$userToAttendeeId[$attendee['actorType']])) {
self::$userToAttendeeId[$attendee['actorType']] = [];
@@ -408,6 +411,9 @@ public function userSeesAttendeesInRoom(string $user, string $identifier, int $s
if (isset($attendee['participantType'])) {
$attendee['participantType'] = (string)$this->mapParticipantTypeTestInput($attendee['participantType']);
}
+ if (isset($attendee['publishingPermissions'])) {
+ $attendee['publishingPermissions'] = (string)$this->mapPublishingPermissionsTestInput($attendee['publishingPermissions']);
+ }
return $attendee;
}, $formData->getHash());
@@ -468,6 +474,22 @@ private function mapParticipantTypeTestInput($participantType) {
Assert::fail('Invalid test input value for participant type');
}
+ private function mapPublishingPermissionsTestInput($publishingPermissions) {
+ if (is_numeric($publishingPermissions)) {
+ return $publishingPermissions;
+ }
+
+ switch ($publishingPermissions) {
+ case 'NONE': return 0;
+ case 'AUDIO': return 1;
+ case 'VIDEO': return 2;
+ case 'SCREENSHARING': return 4;
+ case 'ALL': return 7;
+ }
+
+ Assert::fail('Invalid test input value for publishing permissions');
+ }
+
/**
* @param string $guest
* @param string $isOrNotParticipant
@@ -1055,6 +1077,52 @@ public function userPromoteDemoteInRoom(string $user, string $isPromotion, strin
$this->assertStatusCode($this->response, $statusCode);
}
+ /**
+ * @When /^user "([^"]*)" sets publishing permissions for "([^"]*)" in room "([^"]*)" to "([^"]*)" with (\d+) \((v4)\)$/
+ *
+ * @param string $user
+ * @param string $identifier
+ * @param string $publishingPermissionsString
+ * @param int $statusCode
+ * @param string $apiVersion
+ */
+ public function userSetsPublishingPermissionsForInRoomTo(string $user, string $participant, string $identifier, string $publishingPermissionsString, int $statusCode, string $apiVersion): void {
+ if ($participant === 'stranger') {
+ $attendeeId = 123456789;
+ } elseif (strpos($participant, 'guest') === 0) {
+ $sessionId = self::$userToSessionId[$participant];
+ $attendeeId = $this->getAttendeeId('guests', sha1($sessionId), $identifier, $statusCode === 200 ? $user : null);
+ } else {
+ $attendeeId = $this->getAttendeeId('users', $participant, $identifier, $statusCode === 200 ? $user : null);
+ }
+
+ if ($publishingPermissionsString === 'NONE') {
+ $publishingPermissions = 0;
+ } elseif ($publishingPermissionsString === 'AUDIO') {
+ $publishingPermissions = 1;
+ } elseif ($publishingPermissionsString === 'VIDEO') {
+ $publishingPermissions = 2;
+ } elseif ($publishingPermissionsString === 'SCREENSHARING') {
+ $publishingPermissions = 4;
+ } elseif ($publishingPermissionsString === 'ALL') {
+ $publishingPermissions = 7;
+ } else {
+ Assert::fail('Invalid publishing permissions');
+ }
+
+ $requestParameters = [
+ ['attendeeId', $attendeeId],
+ ['state', $publishingPermissions],
+ ];
+
+ $this->setCurrentUser($user);
+ $this->sendRequest(
+ 'PUT', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/attendees/publishing-permissions',
+ new TableNode($requestParameters)
+ );
+ $this->assertStatusCode($this->response, $statusCode);
+ }
+
/**
* @Then /^user "([^"]*)" joins call "([^"]*)" with (\d+) \((v4)\)$/
*
diff --git a/tests/integration/features/conversation-2/set-publishing-permissions.feature b/tests/integration/features/conversation-2/set-publishing-permissions.feature
new file mode 100644
index 00000000000..b75430684e5
--- /dev/null
+++ b/tests/integration/features/conversation-2/set-publishing-permissions.feature
@@ -0,0 +1,439 @@
+Feature: set-publishing-permissions
+ Background:
+ Given user "owner" exists
+ Given user "moderator" exists
+ Given user "invited user" exists
+ Given user "not invited user" exists
+ Given user "not invited but joined user" exists
+ Given user "not joined user" exists
+
+ Scenario: owner can not set publishing permissions in one-to-one room
+ Given user "owner" creates room "one-to-one room" (v4)
+ | roomType | 1 |
+ | invite | moderator |
+ And user "owner" loads attendees attendee ids in room "one-to-one room" (v4)
+ When user "owner" sets publishing permissions for "owner" in room "one-to-one room" to "NONE" with 400 (v4)
+ And user "owner" sets publishing permissions for "moderator" in room "one-to-one room" to "NONE" with 400 (v4)
+ And user "moderator" sets publishing permissions for "owner" in room "one-to-one room" to "NONE" with 400 (v4)
+ And user "moderator" sets publishing permissions for "moderator" in room "one-to-one room" to "NONE" with 400 (v4)
+ Then user "owner" sees the following attendees in room "one-to-one room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ And user "moderator" sees the following attendees in room "one-to-one room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+
+
+
+ Scenario: owner can set publishing permissions in group room
+ Given user "owner" creates room "group room" (v4)
+ | roomType | 2 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "group room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "group room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "group room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "group room" (v4)
+ When user "owner" sets publishing permissions for "owner" in room "group room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "moderator" in room "group room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "invited user" in room "group room" to "NONE" with 200 (v4)
+ Then user "owner" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ And user "moderator" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ And user "invited user" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+
+ Scenario: moderator can set publishing permissions in group room
+ Given user "owner" creates room "group room" (v4)
+ | roomType | 2 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "group room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "group room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "group room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "group room" (v4)
+ When user "moderator" sets publishing permissions for "owner" in room "group room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "moderator" in room "group room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "invited user" in room "group room" to "NONE" with 200 (v4)
+ Then user "owner" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ And user "moderator" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ And user "invited user" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+
+ Scenario: others can not set publishing permissions in group room
+ Given user "owner" creates room "group room" (v4)
+ | roomType | 2 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "group room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "group room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "group room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "group room" (v4)
+ When user "invited user" sets publishing permissions for "owner" in room "group room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "moderator" in room "group room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "invited user" in room "group room" to "NONE" with 403 (v4)
+ And user "not invited user" sets publishing permissions for "owner" in room "group room" to "NONE" with 404 (v4)
+ And user "not invited user" sets publishing permissions for "moderator" in room "group room" to "NONE" with 404 (v4)
+ And user "not invited user" sets publishing permissions for "invited user" in room "group room" to "NONE" with 404 (v4)
+ # Guest user names in tests must begin with "guest"
+ And user "guest not joined" sets publishing permissions for "owner" in room "group room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "moderator" in room "group room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "invited user" in room "group room" to "NONE" with 404 (v4)
+ Then user "owner" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ And user "moderator" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ And user "invited user" sees the following attendees in room "group room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+
+
+
+ Scenario: owner can set publishing permissions in public room
+ Given user "owner" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "public room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "public room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "public room" with 200 (v4)
+ And user "not invited but joined user" joins room "public room" with 200 (v4)
+ And user "guest moderator" joins room "public room" with 200 (v4)
+ And user "owner" promotes "guest moderator" in room "public room" with 200 (v4)
+ And user "guest" joins room "public room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "public room" (v4)
+ When user "owner" sets publishing permissions for "owner" in room "public room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "invited user" in room "public room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "owner" sets publishing permissions for "guest" in room "public room" to "NONE" with 200 (v4)
+ Then user "owner" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "invited user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+
+ Scenario: moderator can set publishing permissions in public room
+ Given user "owner" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "public room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "public room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "public room" with 200 (v4)
+ And user "not invited but joined user" joins room "public room" with 200 (v4)
+ And user "guest moderator" joins room "public room" with 200 (v4)
+ And user "owner" promotes "guest moderator" in room "public room" with 200 (v4)
+ And user "guest" joins room "public room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "public room" (v4)
+ When user "moderator" sets publishing permissions for "owner" in room "public room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "invited user" in room "public room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "moderator" sets publishing permissions for "guest" in room "public room" to "NONE" with 200 (v4)
+ Then user "owner" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "invited user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ # Guests can not fetch the participant list
+
+ Scenario: guest moderator can set publishing permissions in public room
+ Given user "owner" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "public room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "public room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "public room" with 200 (v4)
+ And user "not invited but joined user" joins room "public room" with 200 (v4)
+ And user "guest moderator" joins room "public room" with 200 (v4)
+ And user "owner" promotes "guest moderator" in room "public room" with 200 (v4)
+ And user "guest" joins room "public room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "public room" (v4)
+ # Guest user names in tests must begin with "guest"
+ When user "guest moderator" sets publishing permissions for "owner" in room "public room" to "NONE" with 200 (v4)
+ And user "guest moderator" sets publishing permissions for "moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "guest moderator" sets publishing permissions for "invited user" in room "public room" to "NONE" with 200 (v4)
+ And user "guest moderator" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 200 (v4)
+ And user "guest moderator" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 200 (v4)
+ And user "guest moderator" sets publishing permissions for "guest" in room "public room" to "NONE" with 200 (v4)
+ Then user "owner" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "invited user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | NONE |
+ | users | moderator | NONE |
+ | users | invited user | NONE |
+ | users | not invited but joined user | NONE |
+ | guests | "guest moderator" | NONE |
+ | guests | "guest" | NONE |
+ # Guests can not fetch the participant list
+
+ Scenario: others can not set publishing permissions in public room
+ Given user "owner" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "owner" adds user "moderator" to room "public room" with 200 (v4)
+ And user "owner" promotes "moderator" in room "public room" with 200 (v4)
+ And user "owner" adds user "invited user" to room "public room" with 200 (v4)
+ And user "not invited but joined user" joins room "public room" with 200 (v4)
+ And user "guest moderator" joins room "public room" with 200 (v4)
+ And user "owner" promotes "guest moderator" in room "public room" with 200 (v4)
+ And user "guest" joins room "public room" with 200 (v4)
+ And user "owner" loads attendees attendee ids in room "public room" (v4)
+ When user "invited user" sets publishing permissions for "owner" in room "public room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "invited user" in room "public room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "invited user" sets publishing permissions for "guest" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "owner" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "invited user" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "not invited but joined user" sets publishing permissions for "guest" in room "public room" to "NONE" with 403 (v4)
+ And user "not joined user" sets publishing permissions for "owner" in room "public room" to "NONE" with 404 (v4)
+ And user "not joined user" sets publishing permissions for "moderator" in room "public room" to "NONE" with 404 (v4)
+ And user "not joined user" sets publishing permissions for "invited user" in room "public room" to "NONE" with 404 (v4)
+ And user "not joined user" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 404 (v4)
+ And user "not joined user" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 404 (v4)
+ And user "not joined user" sets publishing permissions for "guest" in room "public room" to "NONE" with 404 (v4)
+ # Guest user names in tests must begin with "guest"
+ And user "guest" sets publishing permissions for "owner" in room "public room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "invited user" in room "public room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "guest" in room "public room" to "NONE" with 403 (v4)
+ And user "guest not joined" sets publishing permissions for "owner" in room "public room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "moderator" in room "public room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "invited user" in room "public room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "not invited but joined user" in room "public room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "guest moderator" in room "public room" to "NONE" with 404 (v4)
+ And user "guest not joined" sets publishing permissions for "guest" in room "public room" to "NONE" with 404 (v4)
+ Then user "owner" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ | users | not invited but joined user | ALL |
+ | guests | "guest moderator" | ALL |
+ | guests | "guest" | ALL |
+ And user "moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ | users | not invited but joined user | ALL |
+ | guests | "guest moderator" | ALL |
+ | guests | "guest" | ALL |
+ And user "invited user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ | users | not invited but joined user | ALL |
+ | guests | "guest moderator" | ALL |
+ | guests | "guest" | ALL |
+ And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ | users | not invited but joined user | ALL |
+ | guests | "guest moderator" | ALL |
+ | guests | "guest" | ALL |
+ And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner | ALL |
+ | users | moderator | ALL |
+ | users | invited user | ALL |
+ | users | not invited but joined user | ALL |
+ | guests | "guest moderator" | ALL |
+ | guests | "guest" | ALL |
+ # Guests can not fetch the participant list
+
+
+
+ Scenario: participants can not set publishing permissions in room for a share
+ # These users are only needed in very specific tests, so they are not
+ # created in the background step.
+ Given user "owner of file" exists
+ And user "user with access to file" exists
+ And user "owner of file" shares "welcome.txt" with user "user with access to file" with OCS 100
+ And user "user with access to file" accepts last share
+ And user "owner of file" shares "welcome.txt" by link with OCS 100
+ And user "guest" gets the room for last share with 200 (v1)
+ And user "owner of file" joins room "file last share room" with 200 (v4)
+ And user "user with access to file" joins room "file last share room" with 200 (v4)
+ And user "guest" joins room "file last share room" with 200 (v4)
+ And user "owner of file" loads attendees attendee ids in room "file last share room" (v4)
+ When user "owner of file" sets publishing permissions for "owner of file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "owner of file" sets publishing permissions for "user with access to file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "owner of file" sets publishing permissions for "guest" in room "file last share room" to "NONE" with 403 (v4)
+ And user "user with access to file" sets publishing permissions for "owner of file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "user with access to file" sets publishing permissions for "user with access to file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "user with access to file" sets publishing permissions for "guest" in room "file last share room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "owner of file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "user with access to file" in room "file last share room" to "NONE" with 403 (v4)
+ And user "guest" sets publishing permissions for "guest" in room "file last share room" to "NONE" with 403 (v4)
+ Then user "owner of file" sees the following attendees in room "file last share room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner of file | ALL |
+ | users | user with access to file | ALL |
+ | guests | "guest" | ALL |
+ And user "user with access to file" sees the following attendees in room "file last share room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner of file | ALL |
+ | users | user with access to file | ALL |
+ | guests | "guest" | ALL |
+
+
+
+ # This does not make much sense, but there is no real need to block it either.
+ Scenario: owner can set publishing permissions in a password request room
+ # The user is only needed in very specific tests, so it is not created in
+ # the background step.
+ Given user "owner of file" exists
+ And user "owner of file" shares "welcome.txt" by link with OCS 100
+ | password | 123456 |
+ | sendPasswordByTalk | true |
+ And user "guest" creates the password request room for last share with 201 (v1)
+ And user "guest" joins room "password request for last share room" with 200 (v4)
+ And user "owner of file" joins room "password request for last share room" with 200 (v4)
+ And user "owner of file" loads attendees attendee ids in room "password request for last share room" (v4)
+ When user "owner of file" sets publishing permissions for "owner of file" in room "password request for last share room" to "NONE" with 200 (v4)
+ And user "owner of file" sets publishing permissions for "guest" in room "password request for last share room" to "NONE" with 200 (v4)
+ Then user "owner of file" sees the following attendees in room "password request for last share room" with 200 (v4)
+ | actorType | actorId | publishingPermissions |
+ | users | owner of file | NONE |
+ | guests | "guest" | NONE |
diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php
index 347742e3f8d..6538109286f 100644
--- a/tests/php/CapabilitiesTest.php
+++ b/tests/php/CapabilitiesTest.php
@@ -90,6 +90,7 @@ public function setUp(): void {
'geo-location-sharing',
'voice-message-sharing',
'signaling-v3',
+ 'publishing-permissions',
];
}
diff --git a/tests/php/Controller/SignalingControllerTest.php b/tests/php/Controller/SignalingControllerTest.php
index 47bf3157126..8064d8026b9 100644
--- a/tests/php/Controller/SignalingControllerTest.php
+++ b/tests/php/Controller/SignalingControllerTest.php
@@ -29,6 +29,7 @@
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\AttendeeMapper;
use OCA\Talk\Model\SessionMapper;
use OCA\Talk\Participant;
@@ -384,7 +385,13 @@ public function testBackendRoomInvited() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$room->expects($this->once())
->method('getParticipant')
->with($this->userId)
@@ -434,7 +441,13 @@ public function testBackendRoomUserPublic() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$room->expects($this->once())
->method('getParticipant')
->with($this->userId)
@@ -484,7 +497,13 @@ public function testBackendRoomModeratorPublic() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$participant->expects($this->once())
->method('hasModeratorPermissions')
->with(false)
@@ -540,7 +559,13 @@ public function testBackendRoomAnonymousPublic() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$room->expects($this->once())
->method('getParticipantBySession')
->with($sessionId)
@@ -591,7 +616,13 @@ public function testBackendRoomInvitedPublic() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$room->expects($this->once())
->method('getParticipantBySession')
->with($sessionId)
@@ -632,6 +663,78 @@ public function testBackendRoomInvitedPublic() {
], $result->getData());
}
+ public function dataBackendRoomUserPublicPublishingPermissions(): array {
+ return [
+ [Attendee::PUBLISHING_PERMISSIONS_NONE, []],
+ [Attendee::PUBLISHING_PERMISSIONS_AUDIO, ['publish-media']],
+ [Attendee::PUBLISHING_PERMISSIONS_VIDEO, ['publish-media']],
+ [Attendee::PUBLISHING_PERMISSIONS_VIDEO | Attendee::PUBLISHING_PERMISSIONS_VIDEO, ['publish-media']],
+ [Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING, ['publish-screen']],
+ [Attendee::PUBLISHING_PERMISSIONS_AUDIO | Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING, ['publish-media', 'publish-screen']],
+ [Attendee::PUBLISHING_PERMISSIONS_VIDEO | Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING, ['publish-media', 'publish-screen']],
+ [Attendee::PUBLISHING_PERMISSIONS_AUDIO | Attendee::PUBLISHING_PERMISSIONS_VIDEO | Attendee::PUBLISHING_PERMISSIONS_SCREENSHARING, ['publish-media', 'publish-screen']],
+ ];
+ }
+
+ /**
+ * @dataProvider dataBackendRoomUserPublicPublishingPermissions
+ *
+ * @param int $publishingPermissions
+ * @param array $expectedBackendPermissions
+ */
+ public function testBackendRoomUserPublicPublishingPermissions(int $publishingPermissions, array $expectedBackendPermissions) {
+ $roomToken = 'the-room';
+ $roomName = 'the-room-name';
+ $room = $this->createMock(Room::class);
+ $this->manager->expects($this->once())
+ ->method('getRoomByToken')
+ ->with($roomToken)
+ ->willReturn($room);
+
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => $publishingPermissions,
+ ]);
+ $participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
+ $room->expects($this->once())
+ ->method('getParticipant')
+ ->with($this->userId)
+ ->willReturn($participant);
+ $room->expects($this->once())
+ ->method('getToken')
+ ->willReturn($roomToken);
+ $room->expects($this->once())
+ ->method('getPropertiesForSignaling')
+ ->with($this->userId)
+ ->willReturn([
+ 'name' => $roomName,
+ 'type' => Room::PUBLIC_CALL,
+ ]);
+
+ $result = $this->performBackendRequest([
+ 'type' => 'room',
+ 'room' => [
+ 'roomid' => $roomToken,
+ 'userid' => $this->userId,
+ 'sessionid' => '',
+ ],
+ ]);
+ $this->assertSame([
+ 'type' => 'room',
+ 'room' => [
+ 'version' => '1.0',
+ 'roomid' => $roomToken,
+ 'properties' => [
+ 'name' => $roomName,
+ 'type' => Room::PUBLIC_CALL,
+ ],
+ 'permissions' => $expectedBackendPermissions,
+ ],
+ ], $result->getData());
+ }
+
public function testBackendRoomAnonymousOneToOne() {
$roomToken = 'the-room';
$sessionId = 'the-session';
@@ -679,7 +782,13 @@ public function testBackendRoomSessionFromEvent() {
->with($roomToken)
->willReturn($room);
+ $attendee = Attendee::fromRow([
+ 'publishing_permissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ]);
$participant = $this->createMock(Participant::class);
+ $participant->expects($this->any())
+ ->method('getAttendee')
+ ->willReturn($attendee);
$room->expects($this->once())
->method('getParticipant')
->with($this->userId)
diff --git a/tests/php/Signaling/BackendNotifierTest.php b/tests/php/Signaling/BackendNotifierTest.php
index 39dabc43d44..6c3dd92f7c9 100644
--- a/tests/php/Signaling/BackendNotifierTest.php
+++ b/tests/php/Signaling/BackendNotifierTest.php
@@ -27,6 +27,7 @@
use OCA\Talk\Config;
use OCA\Talk\Events\SignalingRoomPropertiesEvent;
use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\AttendeeMapper;
use OCA\Talk\Model\SessionMapper;
use OCA\Talk\Participant;
@@ -665,6 +666,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
],
@@ -674,6 +676,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
],
@@ -698,6 +701,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST_MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
],
],
'users' => [
@@ -706,6 +710,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
[
@@ -713,6 +718,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST_MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
],
],
],
@@ -739,6 +745,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
[
@@ -746,6 +753,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => 0,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
'userId' => $notJoinedUserId,
],
[
@@ -753,6 +761,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST_MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
],
],
],
@@ -771,6 +780,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::USER,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
],
@@ -780,6 +790,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::USER,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
[
@@ -787,6 +798,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => 0,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
'userId' => $notJoinedUserId,
],
[
@@ -794,6 +806,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST_MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
],
],
],
@@ -812,6 +825,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
],
],
'users' => [
@@ -820,6 +834,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $userSession,
'participantType' => Participant::USER,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
'userId' => $this->userId,
],
[
@@ -827,6 +842,7 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => 0,
'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
'userId' => $notJoinedUserId,
],
[
@@ -834,6 +850,51 @@ public function testParticipantsTypeChanged() {
'lastPing' => 0,
'sessionId' => $guestSession,
'participantType' => Participant::GUEST,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ ],
+ ],
+ ],
+ ]);
+
+ $this->controller->clearRequests();
+ $this->participantService->updatePublishingPermissions($room, $guestParticipant, Attendee::PUBLISHING_PERMISSIONS_NONE);
+
+ $this->assertMessageWasSent($room, [
+ 'type' => 'participants',
+ 'participants' => [
+ 'changed' => [
+ [
+ 'permissions' => [],
+ 'inCall' => 0,
+ 'lastPing' => 0,
+ 'sessionId' => $guestSession,
+ 'participantType' => Participant::GUEST,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
+ ],
+ ],
+ 'users' => [
+ [
+ 'inCall' => 0,
+ 'lastPing' => 0,
+ 'sessionId' => $userSession,
+ 'participantType' => Participant::USER,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_ALL,
+ 'userId' => $this->userId,
+ ],
+ [
+ 'inCall' => 0,
+ 'lastPing' => 0,
+ 'sessionId' => 0,
+ 'participantType' => Participant::MODERATOR,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
+ 'userId' => $notJoinedUserId,
+ ],
+ [
+ 'inCall' => 0,
+ 'lastPing' => 0,
+ 'sessionId' => $guestSession,
+ 'participantType' => Participant::GUEST,
+ 'publishingPermissions' => Attendee::PUBLISHING_PERMISSIONS_NONE,
],
],
],