From 0219cf5df0ebc240b71910a9942cc261ea3baaa4 Mon Sep 17 00:00:00 2001 From: Sebastien Marinier Date: Tue, 14 May 2024 11:15:02 +0200 Subject: [PATCH 01/25] feat: list all shared rooms for users and moderators --- lib/Db/Room.php | 3 +++ lib/Db/RoomMapper.php | 31 +++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/Db/Room.php b/lib/Db/Room.php index 7fb2816..1ab048e 100644 --- a/lib/Db/Room.php +++ b/lib/Db/Room.php @@ -74,6 +74,7 @@ class Room extends Entity implements JsonSerializable { public $cleanLayout; public $joinMuted; public $running; + public $permission; public function __construct() { $this->addType('maxParticipants', 'integer'); @@ -86,6 +87,7 @@ class Room extends Entity implements JsonSerializable { $this->addType('cleanLayout', 'boolean'); $this->addType('joinMuted', 'boolean'); $this->addType('running', 'boolean'); + $this->addType('permission', 'integer'); } public function jsonSerialize(): array { @@ -102,6 +104,7 @@ class Room extends Entity implements JsonSerializable { 'everyoneIsModerator' => boolval($this->everyoneIsModerator), 'requireModerator' => boolval($this->requireModerator), 'shared' => boolval($this->shared), + 'permission' => $this->permission, 'moderatorToken' => $this->moderatorToken, 'listenOnly' => boolval($this->listenOnly), 'mediaCheck' => boolval($this->mediaCheck), diff --git a/lib/Db/RoomMapper.php b/lib/Db/RoomMapper.php index a1a9c3c..4d117d9 100644 --- a/lib/Db/RoomMapper.php +++ b/lib/Db/RoomMapper.php @@ -12,6 +12,16 @@ class RoomMapper extends QBMapper { parent::__construct($db, 'bbb_rooms', Room::class); } + private function joinShares(IQueryBuilder $qb): IQueryBuilder { + $qb->select('r.*') + ->from($this->tableName, 'r') + ->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id')) + ->addSelect($qb->createFunction('count(case when `s`.`permission` IN ('. + RoomShare::PERMISSION_ADMIN.','.RoomShare::PERMISSION_MODERATOR.','.RoomShare::PERMISSION_USER + .') then 1 else null end) as shared')); + return $qb; + } + /** * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws DoesNotExistException @@ -19,10 +29,7 @@ class RoomMapper extends QBMapper { public function find(int $id): Room { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('r.*') - ->from($this->tableName, 'r') - ->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id')) - ->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared')) + $this->joinShares($qb) ->where($qb->expr()->eq('r.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))) ->groupBy('r.id'); ; @@ -38,10 +45,7 @@ class RoomMapper extends QBMapper { public function findByUid(string $uid): Room { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('r.*') - ->from($this->tableName, 'r') - ->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id')) - ->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared')) + $this->joinShares($qb) ->where($qb->expr()->eq('r.uid', $qb->createNamedParameter($uid))) ->groupBy('r.id'); ; @@ -70,25 +74,20 @@ class RoomMapper extends QBMapper { public function findAll(string $userId, array $groupIds, array $circleIds): array { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('r.*') - ->from($this->tableName, 'r') - ->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id')) - ->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared')) + $this->joinShares($qb) + ->addSelect($qb->createFunction('min(case when `r`.`user_id` ='. $qb->createNamedParameter($userId).' then '.RoomShare::PERMISSION_ADMIN.' else `s`.`permission` end) as permission')) ->where( $qb->expr()->orX( $qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)), $qb->expr()->andX( - $qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)), $qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_USER, IQueryBuilder::PARAM_INT)), $qb->expr()->eq('s.share_with', $qb->createNamedParameter($userId)) ), $qb->expr()->andX( - $qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)), $qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_GROUP, IQueryBuilder::PARAM_INT)), $qb->expr()->in('s.share_with', $qb->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY)) ), $qb->expr()->andX( - $qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)), $qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)), $qb->expr()->in('s.share_with', $qb->createNamedParameter($circleIds, IQueryBuilder::PARAM_STR_ARRAY)) ) @@ -99,7 +98,7 @@ class RoomMapper extends QBMapper { /** @var array */ return $this->findEntities($qb); } - + /** * @return array */ From 9538c35e7910d5d9b08ffbae82dc0013b0f57f21 Mon Sep 17 00:00:00 2001 From: Sebastien Marinier Date: Wed, 15 May 2024 17:26:56 +0200 Subject: [PATCH 02/25] fix: use querybuilder for user comparaison --- lib/Db/RoomMapper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Db/RoomMapper.php b/lib/Db/RoomMapper.php index 4d117d9..8567ef3 100644 --- a/lib/Db/RoomMapper.php +++ b/lib/Db/RoomMapper.php @@ -75,7 +75,7 @@ class RoomMapper extends QBMapper { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $this->joinShares($qb) - ->addSelect($qb->createFunction('min(case when `r`.`user_id` ='. $qb->createNamedParameter($userId).' then '.RoomShare::PERMISSION_ADMIN.' else `s`.`permission` end) as permission')) + ->addSelect($qb->createFunction('min(case when '.$qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)).' then '.RoomShare::PERMISSION_ADMIN.' else `s`.`permission` end) as permission')) ->where( $qb->expr()->orX( $qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)), From e8464042087c67a60a3d2eb8e4f4d5c89e4c8ad7 Mon Sep 17 00:00:00 2001 From: Sebastien Marinier Date: Wed, 15 May 2024 17:39:59 +0200 Subject: [PATCH 03/25] feat: manage view of rooms for moderators and users --- ts/Common/Api.ts | 1 + ts/Manager/RoomRow.tsx | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/ts/Common/Api.ts b/ts/Common/Api.ts index 6a2c278..cc684ad 100644 --- a/ts/Common/Api.ts +++ b/ts/Common/Api.ts @@ -39,6 +39,7 @@ export interface Room { everyoneIsModerator: boolean; requireModerator: boolean; shared: boolean; + permission: Permission; moderatorToken: string; listenOnly: boolean, mediaCheck: boolean, diff --git a/ts/Manager/RoomRow.tsx b/ts/Manager/RoomRow.tsx index bc6808a..8f2dd26 100644 --- a/ts/Manager/RoomRow.tsx +++ b/ts/Manager/RoomRow.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { api, Recording, Room, Restriction, Access } from '../Common/Api'; +import { api, Recording, Room, Restriction, Access, Permission } from '../Common/Api'; import EditRoom from './EditRoom'; import RecordingRow from './RecordingRow'; import EditableValue from './EditableValue'; @@ -172,8 +172,11 @@ const RoomRow: React.FC = (props) => { return ; } - function edit(field: string, type: 'text' | 'number' = 'text', options?) { - return ; + function edit(field: string, type: 'text' | 'number' = 'text', canEdit: boolean = true, options?) { + return canEdit ? + + : + {room[field]}; } function cloneRow() { @@ -189,6 +192,8 @@ const RoomRow: React.FC = (props) => { const maxParticipantsLimit = props.restriction?.maxParticipants || -1; const minParticipantsLimit = (props.restriction?.maxParticipants || -1) < 1 ? 0 : 1; + const adminRoom = room.permission === null || room.permission === Permission.Admin; + return ( <> @@ -210,7 +215,7 @@ const RoomRow: React.FC = (props) => { - {edit('name')} + {edit('name', 'text', adminRoom)} {room.userId !== OC.currentUser && Avatar} @@ -220,25 +225,40 @@ const RoomRow: React.FC = (props) => { {accessToIcon(room.access)} - {edit('maxParticipants', 'number', {min: minParticipantsLimit, max: maxParticipantsLimit < 0 ? undefined : maxParticipantsLimit})} + {edit('maxParticipants', 'number', adminRoom, {min: minParticipantsLimit, max: maxParticipantsLimit < 0 ? undefined : maxParticipantsLimit})} + {adminRoom && updateRoom('record', event.target.checked)} /> - - - + } + {!adminRoom && + + + + } + + {adminRoom && + + } + + {adminRoom && + + } + + {adminRoom && + } {showRecordings && From 9fed698723581d88cc99d66d0034e722929e874c Mon Sep 17 00:00:00 2001 From: Sebastien Marinier Date: Wed, 29 May 2024 17:09:06 +0200 Subject: [PATCH 04/25] feat: sharing rooms with moderators and users Dialog and permissions management --- lib/Controller/ServerController.php | 8 ++++- ts/Common/Translation.ts | 8 ++++- ts/Manager/App.scss | 4 +++ ts/Manager/EditRoomDialog.tsx | 17 ++++----- ts/Manager/RoomRow.tsx | 30 +++++++++++++--- ts/Manager/ShareWith.tsx | 55 ++++++++++++++++++----------- 6 files changed, 88 insertions(+), 34 deletions(-) diff --git a/lib/Controller/ServerController.php b/lib/Controller/ServerController.php index fd1eaf5..a3c22bf 100644 --- a/lib/Controller/ServerController.php +++ b/lib/Controller/ServerController.php @@ -88,12 +88,18 @@ class ServerController extends Controller { return new DataResponse([], Http::STATUS_NOT_FOUND); } - if (!$this->permission->isAdmin($room, $this->userId)) { + if (!$this->permission->isUser($room, $this->userId)) { return new DataResponse([], Http::STATUS_FORBIDDEN); } $recordings = $this->server->getRecordings($room); + if (!$this->permission->isAdmin($room, $this->userId)) { + $recordings = array_filter($recordings, function ($recording) { + return $recording['published']; + }); + } + return new DataResponse($recordings); } diff --git a/ts/Common/Translation.ts b/ts/Common/Translation.ts index d80eb5d..8d78cf7 100644 --- a/ts/Common/Translation.ts +++ b/ts/Common/Translation.ts @@ -1,4 +1,4 @@ -import { Access } from './Api'; +import { Access, Permission } from './Api'; export const AccessOptions = { [Access.Public]: t('bbb', 'Public'), @@ -8,3 +8,9 @@ export const AccessOptions = { [Access.Internal]: t('bbb', 'Internal'), [Access.InternalRestricted]: t('bbb', 'Internal restricted'), }; + +export const PermissionsOptions = { + [Permission.Admin]: t('bbb', 'admin'), + [Permission.Moderator]: t('bbb', 'moderator'), + [Permission.User]: t('bbb', 'user'), +}; diff --git a/ts/Manager/App.scss b/ts/Manager/App.scss index 1a0458c..96ff82d 100644 --- a/ts/Manager/App.scss +++ b/ts/Manager/App.scss @@ -282,6 +282,10 @@ pre { } } +.bbb-simple-menu { + min-width: auto; +} + .bbb-input-container { display: flex; } diff --git a/ts/Manager/EditRoomDialog.tsx b/ts/Manager/EditRoomDialog.tsx index a0b1bde..d54dbd9 100644 --- a/ts/Manager/EditRoomDialog.tsx +++ b/ts/Manager/EditRoomDialog.tsx @@ -123,17 +123,18 @@ const EditRoomDialog: React.FC = ({ room, restriction, updateProperty, op updateProperty('access', value); })} - {room.access === Access.InternalRestricted &&
- - {descriptions.internalRestrictedShareWith} -
} -
-