diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php
--- a/src/app/Http/Controllers/API/V4/OpenViduController.php
+++ b/src/app/Http/Controllers/API/V4/OpenViduController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\API\V4;
use App\Http\Controllers\Controller;
+use App\OpenVidu\Connection;
use App\OpenVidu\Room;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -115,22 +116,22 @@
*/
public function dismissConnection($id, $conn)
{
- $room = Room::where('name', $id)->first();
+ $connection = Connection::where('id', $conn)->first();
- // This isn't a room, bye bye
- if (!$room) {
- return $this->errorResponse(404, \trans('meet.room-not-found'));
+ // There's no such connection, bye bye
+ if (!$connection || $connection->room->name != $id) {
+ return $this->errorResponse(404, \trans('meet.connection-not-found'));
}
$user = Auth::guard()->user();
- // Only the room owner can do it
- if (!$user || $user->id != $room->user_id) {
+ // Only the room owner can do it (for now)
+ if (!$user || $user->id != $connection->room->user_id) {
return $this->errorResponse(403);
}
- if (!$room->closeOVConnection($conn)) {
- return $this->errorResponse(500, \trans('meet.session-dismiss-connection-error'));
+ if (!$connection->dismiss()) {
+ return $this->errorResponse(500, \trans('meet.connection-dismiss-error'));
}
return response()->json(['status' => 'success']);
@@ -281,28 +282,29 @@
if ($init) {
// Choose the connection role
$canPublish = !empty(request()->input('canPublish'));
- $reqRole = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
- $role = $isOwner ? Room::ROLE_MODERATOR : $reqRole;
+ $role = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
+ if ($isOwner) {
+ $role |= Room::ROLE_MODERATOR;
+ $role |= Room::ROLE_OWNER;
+ }
// Create session token for the current user/connection
- $response = $room->getSessionToken($role, ['canPublish' => $canPublish]);
+ $response = $room->getSessionToken($role);
if (empty($response)) {
return $this->errorResponse(500, \trans('meet.session-join-error'));
}
// Create session token for screen sharing connection
- if ($role != Room::ROLE_SUBSCRIBER && !empty(request()->input('screenShare'))) {
- $add_token = $room->getSessionToken(Room::ROLE_PUBLISHER, ['canPublish' => true]);
+ if (($role & Room::ROLE_PUBLISHER) && !empty(request()->input('screenShare'))) {
+ $add_token = $room->getSessionToken(Room::ROLE_SCREEN);
$response['shareToken'] = $add_token['token'];
}
$response_code = 200;
$response['role'] = $role;
- $response['owner'] = $isOwner;
$response['config'] = $config;
- $response['canPublish'] = $canPublish;
} else {
$response_code = 422;
$response['code'] = 322;
@@ -393,6 +395,9 @@
$room->save();
}
+ // Remove all connections
+ Connection::where('session_id', $sessionId)->delete();
+
break;
}
diff --git a/src/app/OpenVidu/Connection.php b/src/app/OpenVidu/Connection.php
new file mode 100644
--- /dev/null
+++ b/src/app/OpenVidu/Connection.php
@@ -0,0 +1,57 @@
+ 'array',
+ ];
+
+ /**
+ * Dismiss (close) the connection.
+ *
+ * @return bool True on success, False on failure
+ */
+ public function dismiss()
+ {
+ if ($this->room->closeOVConnection($this->id)) {
+ $this->delete();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * The room to which this connection belongs.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function room()
+ {
+ return $this->belongsTo(Room::class, 'room_id', 'id');
+ }
+}
diff --git a/src/app/OpenVidu/Room.php b/src/app/OpenVidu/Room.php
--- a/src/app/OpenVidu/Room.php
+++ b/src/app/OpenVidu/Room.php
@@ -18,13 +18,19 @@
{
use SettingsTrait;
- public const ROLE_MODERATOR = 'MODERATOR';
- public const ROLE_PUBLISHER = 'PUBLISHER';
- public const ROLE_SUBSCRIBER = 'SUBSCRIBER';
+ public const ROLE_SUBSCRIBER = 1 << 0;
+ public const ROLE_PUBLISHER = 1 << 1;
+ public const ROLE_MODERATOR = 1 << 2;
+ public const ROLE_SCREEN = 1 << 3;
+ public const ROLE_OWNER = 1 << 4;
public const REQUEST_ACCEPTED = 'accepted';
public const REQUEST_DENIED = 'denied';
+ private const OV_ROLE_MODERATOR = 'MODERATOR';
+ private const OV_ROLE_PUBLISHER = 'PUBLISHER';
+ private const OV_ROLE_SUBSCRIBER = 'SUBSCRIBER';
+
protected $fillable = [
'user_id',
'name'
@@ -169,14 +175,12 @@
/**
* Create a OpenVidu session (connection) token
*
- * @param string $role User role
- * @param array $data User data to attach to the connection.
- * It will be available client-side for everybody.
+ * @param int $role User role (see self::ROLE_* constants)
*
* @return array|null Token data on success, NULL otherwise
* @throws \Exception if session does not exist
*/
- public function getSessionToken($role = self::ROLE_PUBLISHER, $data = []): ?array
+ public function getSessionToken($role = self::ROLE_SUBSCRIBER): ?array
{
if (!$this->session_id) {
throw new \Exception("The room session does not exist");
@@ -186,16 +190,12 @@
// to make it visible for everyone in a room. So, for example we can
// handle/style subscribers/publishers/moderators differently on the
// client-side. Is this a security issue?
- if (!empty($data)) {
- $data += ['role' => $role];
- } else {
- $data = ['role' => $role];
- }
+ $data = ['role' => $role];
$url = 'sessions/' . $this->session_id . '/connection';
$post = [
'json' => [
- 'role' => $role,
+ 'role' => self::OV_ROLE_PUBLISHER,
'data' => json_encode($data)
]
];
@@ -205,11 +205,26 @@
if ($response->getStatusCode() == 200) {
$json = json_decode($response->getBody(), true);
+ // Extract the 'token' part of the token, it will be used to authenticate the connection.
+ // It will be needed in next iterations e.g. to authenticate moderators that aren't
+ // Kolab4 users (or are just not logged in to Kolab4).
+ // FIXME: we could as well generate our own token for auth purposes
+ parse_str(parse_url($json['token'], PHP_URL_QUERY), $url);
+
+ // Create the connection reference in our database
+ $conn = new Connection();
+ $conn->id = $json['id'];
+ $conn->session_id = $this->session_id;
+ $conn->room_id = $this->id;
+ $conn->role = $role;
+ $conn->metadata = ['token' => $url['token']];
+ $conn->save();
+
return [
'session' => $this->session_id,
'token' => $json['token'],
- 'role' => $json['role'],
'connectionId' => $json['id'],
+ 'role' => $role,
];
}
@@ -324,10 +339,10 @@
/**
* Send a OpenVidu signal to the session participants (connections)
*
- * @param string $name Signal name (type)
- * @param array $data Signal data array
- * @param array|string $target List of target connections, Null for all connections.
- * It can be also a participant role.
+ * @param string $name Signal name (type)
+ * @param array $data Signal data array
+ * @param null|int|string[] $target List of target connections, Null for all connections.
+ * It can be also a participant role.
*
* @return bool True on success, False on failure
* @throws \Exception if session does not exist
@@ -345,26 +360,12 @@
];
// Get connection IDs by participant role
- if (is_string($target)) {
- // TODO: We should probably store this in our database/redis. I foresee a use-case
- // for such a connections store on our side, e.g. keeping participant
- // metadata, e.g. selected language, extra roles like a "language interpreter", etc.
-
- $response = $this->client()->request('GET', 'sessions/' . $this->session_id);
-
- if ($response->getStatusCode() !== 200) {
- return false;
- }
-
- $json = json_decode($response->getBody(), true);
- $connections = [];
-
- foreach ($json['connections']['content'] as $connection) {
- if ($connection['role'] === $target) {
- $connections[] = $connection['id'];
- break;
- }
- }
+ if (is_int($target)) {
+ $connections = Connection::where('room_id', $this->id)
+ ->where('session_id', $this->session_id)
+ ->whereRaw("(role & $target)")
+ ->pluck('id')
+ ->all();
if (empty($connections)) {
return false;
diff --git a/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php b/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2021_01_13_120000_create_openvidu_connections_table.php
@@ -0,0 +1,45 @@
+string('id', 24);
+ $table->string('session_id', 24);
+ $table->bigInteger('room_id')->unsigned();
+ $table->smallInteger('role')->default(0);
+ $table->text('metadata')->nullable(); // should be json, but mariadb
+ $table->timestamps();
+
+ $table->primary('id');
+ $table->foreign('room_id')->references('id')->on('openvidu_rooms')->onDelete('cascade');
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('openvidu_connections');
+ }
+}
diff --git a/src/resources/js/meet/app.js b/src/resources/js/meet/app.js
--- a/src/resources/js/meet/app.js
+++ b/src/resources/js/meet/app.js
@@ -2,6 +2,13 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { OpenVidu } from 'openvidu-browser'
+class Roles {
+ static get SUBSCRIBER() { return 1 << 0; }
+ static get PUBLISHER() { return 1 << 1; }
+ static get MODERATOR() { return 1 << 2; }
+ static get SCREEN() { return 1 << 3; }
+ static get OWNER() { return 1 << 4; }
+}
function Meet(container)
{
@@ -14,7 +21,6 @@
let audioSource = '' // Currently selected microphone
let videoSource = '' // Currently selected camera
let sessionData // Room session metadata
- let role // Current user role
let screenOV // OpenVidu object to initialize a screen sharing session
let screenSession // Session object where the user will connect for screen sharing
@@ -67,12 +73,11 @@
this.switchVideo = switchVideo
this.updateSession = updateSession
-
/**
* Join the room session
*
- * @param data Session metadata and event handlers (session, token, shareToken, nickname,
- * canPublish, chatElement, menuElement, onDestroy, onJoinRequest)
+ * @param data Session metadata and event handlers (session, token, shareToken, nickname, role,
+ * chatElement, menuElement, onDestroy, onJoinRequest)
*/
function joinRoom(data) {
resize();
@@ -97,7 +102,6 @@
session.on('connectionCreated', event => {
// Ignore the current user connection
if (event.connection.role) {
- role = event.connection.role
return
}
@@ -182,7 +186,7 @@
session.connect(data.token, data.params)
.then(() => {
let wrapper
- let params = { self: true, canPublish: data.canPublish, audioActive, videoActive }
+ let params = { self: true, role: data.role, audioActive, videoActive }
params = Object.assign({}, data.params, params)
publisher.on('videoElementCreated', event => {
@@ -196,7 +200,7 @@
wrapper = participantCreate(params)
- if (data.canPublish) {
+ if (data.role & Roles.PUBLISHER) {
publisher.createVideoElement(wrapper, 'PREPEND')
session.publish(publisher)
}
@@ -654,7 +658,7 @@
}
/**
- * Create a participant element in the matrix. Depending on the `canPublish`
+ * Create a participant element in the matrix. Depending on the connection role
* parameter it will be a video element wrapper inside the matrix or a simple
* tag-like element on the subscribers list.
*
@@ -663,7 +667,7 @@
* @return The element
*/
function participantCreate(params) {
- if (params.canPublish) {
+ if (params.role & Roles.PUBLISHER || params.role & Roles.SCREEN) {
return publisherCreate(params)
}
@@ -760,7 +764,7 @@
$element.addClass('self')
}
- if (role == 'MODERATOR') {
+ if (sessionData.role & Roles.MODERATOR) {
$element.addClass('moderated')
}
}
@@ -827,7 +831,7 @@
return false
}
})
- } else if (role == 'MODERATOR') {
+ } else if (sessionData.role & Roles.MODERATOR) {
nickname.attr({title: 'Options', 'data-toggle': 'dropdown'})
.dropdown({boundary: container})
@@ -1071,4 +1075,4 @@
}
}
-export default Meet
+export { Meet, Roles }
diff --git a/src/resources/lang/en/meet.php b/src/resources/lang/en/meet.php
--- a/src/resources/lang/en/meet.php
+++ b/src/resources/lang/en/meet.php
@@ -13,6 +13,8 @@
|
*/
+ 'connection-not-found' => 'The connection does not exist.',
+ 'connection-dismiss-error' => 'Failed to dismiss the connection.',
'room-not-found' => 'The room does not exist.',
'room-setconfig-success' => 'Room configuration updated successfully.',
'room-unsupported-option-error' => 'Invalid room configuration option.',
@@ -21,7 +23,6 @@
'session-join-error' => 'Failed to join the session.',
'session-close-error' => 'Failed to close the session.',
'session-close-success' => 'The session has been closed successfully.',
- 'session-dismiss-connection-error' => 'Failed to dismiss the connection.',
'session-password-error' => 'Failed to join the session. Invalid password.',
'session-request-accept-error' => 'Failed to accept the join request.',
'session-request-deny-error' => 'Failed to deny the join request.',
diff --git a/src/resources/vue/Meet/Room.vue b/src/resources/vue/Meet/Room.vue
--- a/src/resources/vue/Meet/Room.vue
+++ b/src/resources/vue/Meet/Room.vue
@@ -20,7 +20,7 @@
-