Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d2c291a
Add publishing permissions field to attendees
danxuliu May 31, 2021
189b994
Add endpoint to set publishing permissions of attendees
danxuliu May 31, 2021
c5d616a
Add integration tests for getting and setting publishing permissions
danxuliu May 31, 2021
3b73e10
Add API documentation for the publishing permissions property
danxuliu May 31, 2021
7631251
Grant permissions in the HPB based on publishing permissions
danxuliu Feb 8, 2021
02ab305
Fix requested call flags set in the store instead of the actual ones
danxuliu Jun 3, 2021
06258aa
Honour media constraints when joining a call
danxuliu Mar 17, 2020
2f2c673
Fix localMediaStream not including the actual constraints in some cases
danxuliu Jun 3, 2021
4f1bb94
Do not try to request media when no media is allowed to be sent
danxuliu Mar 17, 2020
da5d51e
Send media streams in calls only when publishing is allowed
danxuliu Mar 17, 2020
4009de7
Include "publishingPermissions" in participant messages of signaling
danxuliu Jun 3, 2021
ae85ac0
Listen to local media events only once when joining a call
danxuliu Jun 4, 2021
1a7ae77
Set media as disabled and unavailable when the local stream is stopped
danxuliu Jun 4, 2021
fb3817f
Restore media state only when joining a call
danxuliu Jun 4, 2021
851a965
Handle publishing permission changes during calls
danxuliu Jun 4, 2021
23abb56
Keep track of whether the local media is active or not
danxuliu Jun 7, 2021
dbd1abf
Restrict call flags based on publishing permissions
danxuliu Jun 7, 2021
1588b64
Add capability for "publishing-permissions"
danxuliu Jun 7, 2021
51d1c59
Show tooltips in media buttons when the user does not have permissions
danxuliu Jun 8, 2021
9517a57
Disable audio and video buttons also if publishing is not allowed
danxuliu Jun 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m

]]></description>

<version>12.0.0-alpha.1</version>
<version>12.0.0-alpha.2</version>
<licence>agpl</licence>

<author>Daniel Calviño Sánchez</author>
Expand Down
9 changes: 9 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
6 changes: 6 additions & 0 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
1 change: 1 addition & 0 deletions docs/conversation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions docs/participant.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public function getCapabilities(): array {
'geo-location-sharing',
'voice-message-sharing',
'signaling-v3',
'publishing-permissions',
],
'config' => [
'attachments' => [
Expand Down
27 changes: 27 additions & 0 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => '',
Expand Down Expand Up @@ -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(),
]);
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions lib/Controller/SignalingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
];
}

Expand Down Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions lib/Migration/Version12000Date20210528100404.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2021, Daniel Calviño Sánchez <[email protected]>
*
* @author Daniel Calviño Sánchez <[email protected]>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

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;
}
}
13 changes: 13 additions & 0 deletions lib/Model/Attendee.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand Down Expand Up @@ -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');
Expand All @@ -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 {
Expand All @@ -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(),
];
}
}
1 change: 1 addition & 0 deletions lib/Model/AttendeeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
]);
}
}
1 change: 1 addition & 0 deletions lib/Model/SelectHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
35 changes: 35 additions & 0 deletions lib/Service/ParticipantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
10 changes: 9 additions & 1 deletion lib/Signaling/BackendNotifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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';
Expand Down
Loading