From ad7eedf14b55bcaf9a16c47f1692c1ae3f3cc0e2 Mon Sep 17 00:00:00 2001 From: sualko Date: Wed, 17 Jun 2020 10:56:28 +0200 Subject: [PATCH] feat: add option to share room fix #33 --- lib/Controller/RoomController.php | 52 ++++++++++++++----- lib/Controller/RoomShareController.php | 4 +- lib/Controller/ServerController.php | 10 +++- lib/Db/Room.php | 1 + lib/Db/RoomMapper.php | 34 ++++++++---- lib/Service/RoomService.php | 18 +++---- .../Controller/RoomShareControllerTest.php | 20 +++++-- ts/Manager/Api.ts | 1 + ts/Manager/App.scss | 4 ++ ts/Manager/App.tsx | 1 + ts/Manager/RoomRow.tsx | 11 +++- ts/Manager/ShareWith.scss | 8 +++ ts/Manager/ShareWith.tsx | 28 +++++++--- 13 files changed, 145 insertions(+), 47 deletions(-) diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index b51a9d6..5ca0395 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -2,17 +2,29 @@ namespace OCA\BigBlueButton\Controller; +use OCA\BigBlueButton\Service\RoomService; +use OCA\BigBlueButton\Permission; use OCP\IRequest; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Controller; - -use OCA\BigBlueButton\Service\RoomService; +use OCP\IGroupManager; +use OCP\IUserManager; class RoomController extends Controller { /** @var RoomService */ private $service; + /** @var IUserManager */ + private $userManager; + + /** @var IGroupManager */ + private $groupManager; + + /** @var Permission */ + private $permission; + /** @var string */ private $userId; @@ -22,10 +34,16 @@ class RoomController extends Controller $appName, IRequest $request, RoomService $service, + IUserManager $userManager, + IGroupManager $groupManager, + Permission $permission, $userId ) { parent::__construct($appName, $request); $this->service = $service; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->permission = $permission; $this->userId = $userId; } @@ -34,17 +52,10 @@ class RoomController extends Controller */ public function index(): DataResponse { - return new DataResponse($this->service->findAll($this->userId)); - } + $user = $this->userManager->get($this->userId); + $groupIds = $this->groupManager->getUserGroupIds($user); - /** - * @NoAdminRequired - */ - public function show(int $id): DataResponse - { - return $this->handleNotFound(function () use ($id) { - return $this->service->find($id, $this->userId); - }); + return new DataResponse($this->service->findAll($this->userId, $groupIds)); } /** @@ -77,8 +88,14 @@ class RoomController extends Controller string $access, bool $everyoneIsModerator ): DataResponse { + $room = $this->service->find($id); + + if (!$this->permission->isAdmin($room, $this->userId)) { + return new DataResponse(null, Http::STATUS_FORBIDDEN); + } + return $this->handleNotFound(function () use ($id, $name, $welcome, $maxParticipants, $record, $everyoneIsModerator, $access) { - return $this->service->update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $this->userId); + return $this->service->update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator); }); } @@ -87,8 +104,15 @@ class RoomController extends Controller */ public function destroy(int $id): DataResponse { + $room = $this->service->find($id); + + if (!$this->permission->isAdmin($room, $this->userId)) { + return new DataResponse(null, Http::STATUS_FORBIDDEN); + } + return $this->handleNotFound(function () use ($id) { - return $this->service->delete($id, $this->userId); + //@TODO delete shares + return $this->service->delete($id); }); } } diff --git a/lib/Controller/RoomShareController.php b/lib/Controller/RoomShareController.php index d545ee2..e6acfa4 100644 --- a/lib/Controller/RoomShareController.php +++ b/lib/Controller/RoomShareController.php @@ -143,9 +143,9 @@ class RoomShareController extends Controller private function isUserAllowed(int $roomId): bool { try { - $room = $this->roomService->find($roomId, $this->userId); + $room = $this->roomService->find($roomId); - return $room !== null; + return $room->getUserId() === $this->userId; } catch (RoomShareNotFound $e) { return false; } diff --git a/lib/Controller/ServerController.php b/lib/Controller/ServerController.php index 72f4d80..455c808 100644 --- a/lib/Controller/ServerController.php +++ b/lib/Controller/ServerController.php @@ -3,6 +3,7 @@ namespace OCA\BigBlueButton\Controller; use OCA\BigBlueButton\BigBlueButton\API; +use OCA\BigBlueButton\Permission; use OCP\IRequest; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -18,6 +19,9 @@ class ServerController extends Controller /** @var API */ private $server; + /** @var Permission */ + private $permission; + /** @var string */ private $userId; @@ -26,12 +30,14 @@ class ServerController extends Controller IRequest $request, RoomService $service, API $server, + Permission $permission, $UserId ) { parent::__construct($appName, $request); $this->service = $service; $this->server = $server; + $this->permission = $permission; $this->userId = $UserId; } @@ -46,7 +52,7 @@ class ServerController extends Controller return new DataResponse([], Http::STATUS_NOT_FOUND); } - if ($room->userId !== $this->userId) { + if (!$this->permission->isAdmin($room, $this->userId)) { return new DataResponse([], Http::STATUS_FORBIDDEN); } @@ -68,7 +74,7 @@ class ServerController extends Controller return new DataResponse(false, Http::STATUS_NOT_FOUND); } - if ($room->userId !== $this->userId) { + if (!$this->permission->isAdmin($room, $this->userId)) { return new DataResponse(false, Http::STATUS_FORBIDDEN); } diff --git a/lib/Db/Room.php b/lib/Db/Room.php index e2f5340..2fb26cf 100644 --- a/lib/Db/Room.php +++ b/lib/Db/Room.php @@ -37,6 +37,7 @@ class Room extends Entity implements JsonSerializable return [ 'id' => $this->id, 'uid' => $this->uid, + 'userId' => $this->userId, 'name' => $this->name, 'welcome' => $this->welcome, 'maxParticipants' => (int) $this->maxParticipants, diff --git a/lib/Db/RoomMapper.php b/lib/Db/RoomMapper.php index ea728d9..4d708d4 100644 --- a/lib/Db/RoomMapper.php +++ b/lib/Db/RoomMapper.php @@ -21,14 +21,13 @@ class RoomMapper extends QBMapper * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws DoesNotExistException */ - public function find(int $id, string $userId): Room + public function find(int $id): Room { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->select('*') - ->from('bbb_rooms') - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); return $this->findEntity($qb); } @@ -43,22 +42,39 @@ class RoomMapper extends QBMapper /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->select('*') - ->from('bbb_rooms') + ->from($this->tableName) ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); return $this->findEntity($qb); } /** * @param int $userId + * @param array $groupIds * @return array */ - public function findAll(string $userId): array + public function findAll(string $userId, array $groupIds): array { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from('bbb_rooms') - ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + $qb->select('r.*') + ->from($this->tableName, 'r') + ->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id')) + ->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', $groupIds) + ) + ) + ) + ->groupBy('r.id'); return $this->findEntities($qb); } } diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 6bcf573..6598771 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -8,6 +8,7 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCA\BigBlueButton\Db\Room; use OCA\BigBlueButton\Db\RoomMapper; +use OCA\BigBlueButton\NoPermissionException; class RoomService { @@ -20,9 +21,9 @@ class RoomService $this->mapper = $mapper; } - public function findAll(string $userId): array + public function findAll(string $userId, array $groupIds): array { - return $this->mapper->findAll($userId); + return $this->mapper->findAll($userId, $groupIds); } private function handleException(Exception $e): void @@ -35,10 +36,10 @@ class RoomService } } - public function find($id, $userId) + public function find($id): Room { try { - return $this->mapper->find($id, $userId); + return $this->mapper->find($id); // in order to be able to plug in different storage backends like files // for instance it is a good idea to turn storage related exceptions @@ -75,10 +76,10 @@ class RoomService return $this->mapper->insert($room); } - public function update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $userId) + public function update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator) { try { - $room = $this->mapper->find($id, $userId); + $room = $this->mapper->find($id); if ($room->access !== $access) { $room->setPassword($access === Room::ACCESS_PASSWORD ? $this->humanReadableRandom(8) : null); @@ -90,7 +91,6 @@ class RoomService $room->setRecord($record); $room->setAccess($access); $room->setEveryoneIsModerator($everyoneIsModerator); - $room->setUserId($userId); return $this->mapper->update($room); } catch (Exception $e) { @@ -98,10 +98,10 @@ class RoomService } } - public function delete($id, $userId) + public function delete($id) { try { - $room = $this->mapper->find($id, $userId); + $room = $this->mapper->find($id); $this->mapper->delete($room); return $room; } catch (Exception $e) { diff --git a/tests/Unit/Controller/RoomShareControllerTest.php b/tests/Unit/Controller/RoomShareControllerTest.php index 8fedae0..6b0c6c5 100644 --- a/tests/Unit/Controller/RoomShareControllerTest.php +++ b/tests/Unit/Controller/RoomShareControllerTest.php @@ -21,6 +21,8 @@ class RoomShareControllerTest extends TestCase private $userManager; private $controller; + private $userId = 'user_foo'; + public function setUp(): void { parent::setUp(); @@ -36,7 +38,7 @@ class RoomShareControllerTest extends TestCase $this->service, $this->userManager, $this->roomService, - 'user_foo' + $this->userId ); } @@ -55,10 +57,14 @@ class RoomShareControllerTest extends TestCase ->with('id') ->willReturn(1234); + $room = new Room(); + $room->setUserId('user_bar'); + $this->roomService ->expects($this->once()) ->method('find') - ->will($this->throwException(new RoomShareNotFound)); + ->with(1234) + ->willReturn($room); $response = $this->controller->index(); @@ -74,10 +80,13 @@ class RoomShareControllerTest extends TestCase ->with('id') ->willReturn($roomId); + $room = new Room(); + $room->setUserId($this->userId); + $this->roomService ->expects($this->once()) ->method('find') - ->willReturn(new Room()); + ->willReturn($room); $this->service ->expects($this->once()) @@ -100,10 +109,13 @@ class RoomShareControllerTest extends TestCase ->with('id') ->willReturn($roomId); + $room = new Room(); + $room->setUserId($this->userId); + $this->roomService ->expects($this->once()) ->method('find') - ->willReturn(new Room()); + ->willReturn($room); $this->service ->expects($this->once()) diff --git a/ts/Manager/Api.ts b/ts/Manager/Api.ts index b2e1010..e33f116 100644 --- a/ts/Manager/Api.ts +++ b/ts/Manager/Api.ts @@ -15,6 +15,7 @@ export enum Access { export interface Room { id: number; uid: string; + userId: string; name: string; welcome: string; maxParticipants: number; diff --git a/ts/Manager/App.scss b/ts/Manager/App.scss index 17cb916..34386db 100644 --- a/ts/Manager/App.scss +++ b/ts/Manager/App.scss @@ -15,6 +15,10 @@ margin-top: 1em; } +.bbb-avatar { + border-radius: 50%; +} + #bbb-warning { padding: 1em; background-color: rgb(255, 255, 123); diff --git a/ts/Manager/App.tsx b/ts/Manager/App.tsx index ae7ea1f..10f94b1 100644 --- a/ts/Manager/App.tsx +++ b/ts/Manager/App.tsx @@ -107,6 +107,7 @@ const App: React.FC = () => { onOrderBy('name')}> {t('bbb', 'Name')} + onOrderBy('maxParticipants')}> {t('bbb', 'Max')} diff --git a/ts/Manager/RoomRow.tsx b/ts/Manager/RoomRow.tsx index da76d23..46b949f 100644 --- a/ts/Manager/RoomRow.tsx +++ b/ts/Manager/RoomRow.tsx @@ -158,6 +158,12 @@ const RoomRow: React.FC = (props) => { return ; } + const avatarUrl = OC.generateUrl('/avatar/' + encodeURIComponent(room.userId) + '/' + 24, { + user: room.userId, + size: 24, + requesttoken: OC.requestToken, + }); + return ( <> @@ -175,6 +181,9 @@ const RoomRow: React.FC = (props) => { {edit('name')} + + {room.userId !== OC.currentUser && Avatar} + {edit('maxParticipants', 'number')} @@ -193,7 +202,7 @@ const RoomRow: React.FC = (props) => { {showRecordings && - + {recordings?.map(recording => )} diff --git a/ts/Manager/ShareWith.scss b/ts/Manager/ShareWith.scss index c275762..71f6047 100644 --- a/ts/Manager/ShareWith.scss +++ b/ts/Manager/ShareWith.scss @@ -61,4 +61,12 @@ .bbb-form-shareWith { margin-top: -1.5em; +} + +.bbb-icon-unselected { + opacity: 0.2 !important; + + &:hover { + opacity: 0.5 !important; + } } \ No newline at end of file diff --git a/ts/Manager/ShareWith.tsx b/ts/Manager/ShareWith.tsx index 76e142b..30d2b98 100644 --- a/ts/Manager/ShareWith.tsx +++ b/ts/Manager/ShareWith.tsx @@ -15,6 +15,8 @@ const ShareWith: React.FC = ({ room, permission, shares: allShares, setSh const [recommendations, setRecommendations] = useState(); const [searchResults, setSearchResults] = useState(); + const isOwner = room.userId === OC.currentUser; + const shares = (allShares && permission === Permission.Moderator) ? allShares.filter(share => share.permission !== Permission.User) : allShares; @@ -31,7 +33,7 @@ const ShareWith: React.FC = ({ room, permission, shares: allShares, setSh api.getRecommendedShareWith().then(result => setRecommendations(result)); }, []); - async function addRoomShare(shareWith: string, shareType: number, displayName: string) { + async function addRoomShare(shareWith: string, shareType: number, displayName: string, permission: Permission) { const roomShare = await api.createRoomShare(room.id, shareType, shareWith, permission); roomShare.shareWithDisplayName = displayName; @@ -60,6 +62,12 @@ const ShareWith: React.FC = ({ room, permission, shares: allShares, setSh setShares((allShares ? [...allShares] : []).filter(share => share.id !== id)); } + async function toggleAdminShare(share: RoomShare) { + const newPermission = share.permission === Permission.Admin ? Permission.Moderator : Permission.Admin; + + return addRoomShare(share.shareWith, share.shareType, share.shareWithDisplayName || share.shareWith, newPermission); + } + function renderSearchResults(options: ShareWith) { return (