mirror of https://github.com/sualko/cloud_bbb
parent
bec1b4dce7
commit
8b2dc9cb71
|
@ -11,6 +11,7 @@ use BigBlueButton\Parameters\DeleteRecordingsParameters;
|
||||||
use BigBlueButton\Parameters\IsMeetingRunningParameters;
|
use BigBlueButton\Parameters\IsMeetingRunningParameters;
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
use OCA\BigBlueButton\Db\RoomShare;
|
use OCA\BigBlueButton\Db\RoomShare;
|
||||||
|
use OCA\BigBlueButton\Permission;
|
||||||
use OCA\BigBlueButton\Service\RoomShareService;
|
use OCA\BigBlueButton\Service\RoomShareService;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
@ -24,11 +25,8 @@ class API
|
||||||
/** @var IURLGenerator */
|
/** @var IURLGenerator */
|
||||||
private $urlGenerator;
|
private $urlGenerator;
|
||||||
|
|
||||||
/** @var IGroupManager */
|
/** @var Permission */
|
||||||
private $groupManager;
|
private $permission;
|
||||||
|
|
||||||
/** @var RoomShareService */
|
|
||||||
private $roomShareService;
|
|
||||||
|
|
||||||
/** @var BigBlueButton */
|
/** @var BigBlueButton */
|
||||||
private $server;
|
private $server;
|
||||||
|
@ -36,13 +34,11 @@ class API
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IConfig $config,
|
IConfig $config,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
IGroupManager $groupManager,
|
Permission $permission
|
||||||
RoomShareService $roomShareService
|
|
||||||
) {
|
) {
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->groupManager = $groupManager;
|
$this->permission = $permission;
|
||||||
$this->roomShareService = $roomShareService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getServer()
|
private function getServer()
|
||||||
|
@ -64,7 +60,7 @@ class API
|
||||||
*/
|
*/
|
||||||
public function createJoinUrl(Room $room, int $creationTime, string $displayname, string $uid = null)
|
public function createJoinUrl(Room $room, int $creationTime, string $displayname, string $uid = null)
|
||||||
{
|
{
|
||||||
$password = $this->isModerator($room, $uid) ? $room->moderatorPassword : $room->attendeePassword;
|
$password = $this->permission->isModerator($room, $uid) ? $room->moderatorPassword : $room->attendeePassword;
|
||||||
|
|
||||||
$joinMeetingParams = new JoinMeetingParameters($room->uid, $displayname, $password);
|
$joinMeetingParams = new JoinMeetingParameters($room->uid, $displayname, $password);
|
||||||
|
|
||||||
|
@ -81,38 +77,6 @@ class API
|
||||||
return $this->getServer()->getJoinMeetingURL($joinMeetingParams);
|
return $this->getServer()->getJoinMeetingURL($joinMeetingParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isModerator(Room $room, string $uid): bool
|
|
||||||
{
|
|
||||||
if ($uid === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($uid === $room->userId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$shares = $this->roomShareService->findAll($room->id);
|
|
||||||
|
|
||||||
/** @var RoomShare $share */
|
|
||||||
foreach ($shares as $share) {
|
|
||||||
if (!$share->hasModeratorPermission()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($share->getShareType() === RoomShare::SHARE_TYPE_USER) {
|
|
||||||
if ($share->getShareWith() === $uid) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} elseif ($share->getShareType() === RoomShare::SHARE_TYPE_GROUP) {
|
|
||||||
if ($this->groupManager->isInGroup($uid, $share->getShareWith())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create meeting room.
|
* Create meeting room.
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,7 +4,9 @@ namespace OCA\BigBlueButton\Controller;
|
||||||
use OCA\BigBlueButton\BigBlueButton\API;
|
use OCA\BigBlueButton\BigBlueButton\API;
|
||||||
use OCA\BigBlueButton\BigBlueButton\Presentation;
|
use OCA\BigBlueButton\BigBlueButton\Presentation;
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
|
use OCA\BigBlueButton\NoPermissionException;
|
||||||
use OCA\BigBlueButton\NotFoundException;
|
use OCA\BigBlueButton\NotFoundException;
|
||||||
|
use OCA\BigBlueButton\Permission;
|
||||||
use OCP\AppFramework\Http\RedirectResponse;
|
use OCP\AppFramework\Http\RedirectResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\ISession;
|
use OCP\ISession;
|
||||||
|
@ -38,6 +40,9 @@ class JoinController extends Controller
|
||||||
/** @var API */
|
/** @var API */
|
||||||
private $api;
|
private $api;
|
||||||
|
|
||||||
|
/** @var Permission */
|
||||||
|
private $permission;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $appName,
|
string $appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
|
@ -46,7 +51,8 @@ class JoinController extends Controller
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
IUserSession $userSession,
|
IUserSession $userSession,
|
||||||
IConfig $config,
|
IConfig $config,
|
||||||
API $api
|
API $api,
|
||||||
|
Permission $permission
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request, $session);
|
parent::__construct($appName, $request, $session);
|
||||||
|
|
||||||
|
@ -55,6 +61,7 @@ class JoinController extends Controller
|
||||||
$this->userSession = $userSession;
|
$this->userSession = $userSession;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->api = $api;
|
$this->api = $api;
|
||||||
|
$this->permission = $permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setToken(string $token)
|
public function setToken(string $token)
|
||||||
|
@ -90,10 +97,14 @@ class JoinController extends Controller
|
||||||
$displayname = $user->getDisplayName();
|
$displayname = $user->getDisplayName();
|
||||||
$userId = $user->getUID();
|
$userId = $user->getUID();
|
||||||
|
|
||||||
|
if ($room->access == Room::ACCESS_INTERNAL_RESTRICTED && !$this->permission->isUser($room, $userId)) {
|
||||||
|
throw new NoPermissionException();
|
||||||
|
}
|
||||||
|
|
||||||
if ($userId === $room->userId) {
|
if ($userId === $room->userId) {
|
||||||
$presentation = new Presentation($u, $filename);
|
$presentation = new Presentation($u, $filename);
|
||||||
}
|
}
|
||||||
} elseif ($room->access === Room::ACCESS_INTERNAL) {
|
} elseif ($room->access === Room::ACCESS_INTERNAL || $room->access == Room::ACCESS_INTERNAL_RESTRICTED) {
|
||||||
return new RedirectResponse(
|
return new RedirectResponse(
|
||||||
$this->urlGenerator->linkToRoute('core.login.showLoginForm', [
|
$this->urlGenerator->linkToRoute('core.login.showLoginForm', [
|
||||||
'redirect_url' => $this->urlGenerator->linkToRoute(
|
'redirect_url' => $this->urlGenerator->linkToRoute(
|
||||||
|
|
|
@ -30,6 +30,19 @@ class RoomShareMapper extends QBMapper
|
||||||
return $this->findEntity($qb);
|
return $this->findEntity($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByRoomAndEntity(int $roomId, string $shareWith, int $shareType): RoomShare
|
||||||
|
{
|
||||||
|
/* @var $qb IQueryBuilder */
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('*')
|
||||||
|
->from('bbb_room_shares')
|
||||||
|
->where($qb->expr()->eq('room_id', $qb->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($shareWith)))
|
||||||
|
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
}
|
||||||
|
|
||||||
public function findAll(int $roomId): array
|
public function findAll(int $roomId): array
|
||||||
{
|
{
|
||||||
/* @var $qb IQueryBuilder */
|
/* @var $qb IQueryBuilder */
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
namespace OCA\BigBlueButton\Middleware;
|
namespace OCA\BigBlueButton\Middleware;
|
||||||
|
|
||||||
use OCA\BigBlueButton\Controller\JoinController;
|
use OCA\BigBlueButton\Controller\JoinController;
|
||||||
|
use OCA\BigBlueButton\NoPermissionException;
|
||||||
|
use OCA\BigBlueButton\NoPermissionResponse;
|
||||||
use OCA\BigBlueButton\NotFoundException;
|
use OCA\BigBlueButton\NotFoundException;
|
||||||
use OCP\AppFramework\Middleware;
|
use OCP\AppFramework\Middleware;
|
||||||
use OCP\AppFramework\Http\NotFoundResponse;
|
use OCP\AppFramework\Http\NotFoundResponse;
|
||||||
|
@ -47,6 +49,10 @@ class JoinMiddleware extends Middleware
|
||||||
return new NotFoundResponse();
|
return new NotFoundResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($exception instanceof NoPermissionException) {
|
||||||
|
return new NoPermissionResponse();
|
||||||
|
}
|
||||||
|
|
||||||
throw $exception;
|
throw $exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
|
class NoPermissionException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
|
use OCP\Template;
|
||||||
|
use OCP\AppFramework\Http\Response;
|
||||||
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
|
|
||||||
|
class NoPermissionResponse extends Response
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->setContentSecurityPolicy(new ContentSecurityPolicy());
|
||||||
|
$this->setStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$template = new Template('core', '403', 'guest');
|
||||||
|
return $template->fetchPage();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use OCA\BigBlueButton\Service\RoomShareService;
|
||||||
|
use OCA\BigBlueButton\Db\Room;
|
||||||
|
use OCA\BigBlueButton\Db\RoomShare;
|
||||||
|
use OCP\IGroupManager;
|
||||||
|
|
||||||
|
class Permission
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var IGroupManager */
|
||||||
|
private $groupManager;
|
||||||
|
|
||||||
|
/** @var RoomShareService */
|
||||||
|
private $roomShareService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IGroupManager $groupManager,
|
||||||
|
RoomShareService $roomShareService
|
||||||
|
) {
|
||||||
|
$this->groupManager = $groupManager;
|
||||||
|
$this->roomShareService = $roomShareService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isUser(Room $room, string $uid)
|
||||||
|
{
|
||||||
|
return $this->hasPermission($room, $uid, function (RoomShare $share) {
|
||||||
|
return $share->hasUserPermission();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isModerator(Room $room, string $uid)
|
||||||
|
{
|
||||||
|
return $this->hasPermission($room, $uid, function (RoomShare $share) {
|
||||||
|
return $share->hasModeratorPermission();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAdmin(Room $room, string $uid)
|
||||||
|
{
|
||||||
|
return $this->hasPermission($room, $uid, function (RoomShare $share) {
|
||||||
|
return $share->hasAdminPermission();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasPermission(Room $room, string $uid, Closure $hasPermission): bool
|
||||||
|
{
|
||||||
|
if ($uid === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($uid === $room->userId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$shares = $this->roomShareService->findAll($room->id);
|
||||||
|
|
||||||
|
/** @var RoomShare $share */
|
||||||
|
foreach ($shares as $share) {
|
||||||
|
if (!$hasPermission($share)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($share->getShareType() === RoomShare::SHARE_TYPE_USER) {
|
||||||
|
if ($share->getShareWith() === $uid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} elseif ($share->getShareType() === RoomShare::SHARE_TYPE_GROUP) {
|
||||||
|
if ($this->groupManager->isInGroup($uid, $share->getShareWith())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,6 +46,11 @@ class RoomShareService
|
||||||
|
|
||||||
public function create(int $roomId, int $shareType, string $shareWith, int $permission): RoomShare
|
public function create(int $roomId, int $shareType, string $shareWith, int $permission): RoomShare
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$roomShare = $this->mapper->findByRoomAndEntity($roomId, $shareWith, $shareType);
|
||||||
|
|
||||||
|
return $this->update($roomShare->getId(), $roomId, $shareType, $shareWith, $permission);
|
||||||
|
} catch (DoesNotExistException $e) {
|
||||||
$roomShare = new RoomShare();
|
$roomShare = new RoomShare();
|
||||||
|
|
||||||
$roomShare->setRoomId($roomId);
|
$roomShare->setRoomId($roomId);
|
||||||
|
@ -55,6 +60,7 @@ class RoomShareService
|
||||||
|
|
||||||
return $this->mapper->insert($roomShare);
|
return $this->mapper->insert($roomShare);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function update(int $id, int $roomId, int $shareType, string $shareWith, int $permission): RoomShare
|
public function update(int $id, int $roomId, int $shareType, string $shareWith, int $permission): RoomShare
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@ use OCA\BigBlueButton\Controller\JoinController;
|
||||||
use OCA\BigBlueButton\BigBlueButton\API;
|
use OCA\BigBlueButton\BigBlueButton\API;
|
||||||
use OCA\BigBlueButton\NotFoundException;
|
use OCA\BigBlueButton\NotFoundException;
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
|
use OCA\BigBlueButton\Permission;
|
||||||
|
|
||||||
class JoinControllerTest extends TestCase
|
class JoinControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,7 @@ class JoinControllerTest extends TestCase
|
||||||
private $urlGenerator;
|
private $urlGenerator;
|
||||||
private $controller;
|
private $controller;
|
||||||
private $api;
|
private $api;
|
||||||
|
private $permission;
|
||||||
private $room;
|
private $room;
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
|
@ -38,6 +40,7 @@ class JoinControllerTest extends TestCase
|
||||||
$this->config = $this->createMock(IConfig::class);
|
$this->config = $this->createMock(IConfig::class);
|
||||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||||
$this->api = $this->createMock(API::class);
|
$this->api = $this->createMock(API::class);
|
||||||
|
$this->permission = $this->createMock(Permission::class);
|
||||||
|
|
||||||
$this->controller = new JoinController(
|
$this->controller = new JoinController(
|
||||||
'bbb',
|
'bbb',
|
||||||
|
@ -47,7 +50,8 @@ class JoinControllerTest extends TestCase
|
||||||
$this->urlGenerator,
|
$this->urlGenerator,
|
||||||
$this->userSession,
|
$this->userSession,
|
||||||
$this->config,
|
$this->config,
|
||||||
$this->api
|
$this->api,
|
||||||
|
$this->permission
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->room = new Room();
|
$this->room = new Room();
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Room } from './Api';
|
||||||
|
import EditRoomDialog from './EditRoomDialog';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
room: Room;
|
||||||
|
updateProperty: (key: string, value: string | boolean | number) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditRoom: React.FC<Props> = ({ room, updateProperty }) => {
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<a className="icon icon-edit icon-visible"
|
||||||
|
onClick={ev => { ev.preventDefault(), setOpen(true); }}
|
||||||
|
title={t('bbb', 'Edit')} />
|
||||||
|
|
||||||
|
<EditRoomDialog room={room} updateProperty={updateProperty} open={open} setOpen={setOpen} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditRoom;
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Access, Room } from './Api';
|
import { Access, Room, Permission, RoomShare, api } from './Api';
|
||||||
import Dialog from './Dialog';
|
import Dialog from './Dialog';
|
||||||
import ShareWith from './ShareWith';
|
import ShareWith from './ShareWith';
|
||||||
import { SubmitInput } from './SubmitInput';
|
import { SubmitInput } from './SubmitInput';
|
||||||
|
@ -15,10 +15,27 @@ const descriptions: { [key: string]: string } = {
|
||||||
type Props = {
|
type Props = {
|
||||||
room: Room;
|
room: Room;
|
||||||
updateProperty: (key: string, value: string | boolean | number) => Promise<void>;
|
updateProperty: (key: string, value: string | boolean | number) => Promise<void>;
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditRoomDialog: React.FC<Props> = ({ room, updateProperty }) => {
|
const EditRoomDialog: React.FC<Props> = ({ room, updateProperty, open, setOpen }) => {
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [shares, setShares] = useState<RoomShare[]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.getRoomShares(room.id).then(roomShares => {
|
||||||
|
console.log(room.name, roomShares);
|
||||||
|
setShares(roomShares);
|
||||||
|
}).catch(err => {
|
||||||
|
console.warn('Could not load room shares.', err);
|
||||||
|
|
||||||
|
setShares([]);
|
||||||
|
});
|
||||||
|
}, [room.id, open]);
|
||||||
|
|
||||||
function inputElement(label: string, field: string, type: 'text' | 'number' = 'text') {
|
function inputElement(label: string, field: string, type: 'text' | 'number' = 'text') {
|
||||||
return (
|
return (
|
||||||
|
@ -33,7 +50,7 @@ const EditRoomDialog: React.FC<Props> = ({ room, updateProperty }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectElement(label: string, field: string, value: string, options: {[key: string]: string}, onChange: (value: string) => void) {
|
function selectElement(label: string, field: string, value: string, options: { [key: string]: string }, onChange: (value: string) => void) {
|
||||||
return (
|
return (
|
||||||
<div className="bbb-form-element">
|
<div className="bbb-form-element">
|
||||||
<label htmlFor={`bbb-${field}`}>
|
<label htmlFor={`bbb-${field}`}>
|
||||||
|
@ -54,11 +71,6 @@ const EditRoomDialog: React.FC<Props> = ({ room, updateProperty }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<a className="icon icon-edit icon-visible"
|
|
||||||
onClick={ev => { ev.preventDefault(), setOpen(true); }}
|
|
||||||
title={t('bbb', 'Edit')} />
|
|
||||||
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)} title={t('bbb', 'Edit "{room}"', { room: room.name })}>
|
<Dialog open={open} onClose={() => setOpen(false)} title={t('bbb', 'Edit "{room}"', { room: room.name })}>
|
||||||
{inputElement(t('bbb', 'Name'), 'name')}
|
{inputElement(t('bbb', 'Name'), 'name')}
|
||||||
{inputElement(t('bbb', 'Welcome'), 'welcome')}
|
{inputElement(t('bbb', 'Welcome'), 'welcome')}
|
||||||
|
@ -69,18 +81,22 @@ const EditRoomDialog: React.FC<Props> = ({ room, updateProperty }) => {
|
||||||
[Access.Password]: t('bbb', 'Internal + Password protection for guests'),
|
[Access.Password]: t('bbb', 'Internal + Password protection for guests'),
|
||||||
[Access.WaitingRoom]: t('bbb', 'Internal + Waiting room for guests'),
|
[Access.WaitingRoom]: t('bbb', 'Internal + Waiting room for guests'),
|
||||||
[Access.Internal]: t('bbb', 'Internal'),
|
[Access.Internal]: t('bbb', 'Internal'),
|
||||||
// [Access.InternalRestricted]: t('bbb', 'Restricted'),
|
[Access.InternalRestricted]: t('bbb', 'Internal restricted'),
|
||||||
}, (value) => {
|
}, (value) => {
|
||||||
console.log('access', value);
|
console.log('access', value);
|
||||||
updateProperty('access', value);
|
updateProperty('access', value);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{room.access === Access.InternalRestricted && <div className="bbb-form-element bbb-form-shareWith">
|
||||||
|
<ShareWith permission={Permission.User} room={room} shares={shares} setShares={setShares} />
|
||||||
|
</div>}
|
||||||
|
|
||||||
<div className="bbb-form-element">
|
<div className="bbb-form-element">
|
||||||
<label htmlFor={'bbb-moderator'}>
|
<label htmlFor={'bbb-moderator'}>
|
||||||
<h3>Moderator</h3>
|
<h3>Moderator</h3>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ShareWith room={room} />
|
<ShareWith permission={Permission.Moderator} room={room} shares={shares} setShares={setShares} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>{t('bbb', 'Miscellaneous')}</h3>
|
<h3>{t('bbb', 'Miscellaneous')}</h3>
|
||||||
|
@ -96,7 +112,6 @@ const EditRoomDialog: React.FC<Props> = ({ room, updateProperty }) => {
|
||||||
<em>{descriptions.recording}</em>
|
<em>{descriptions.recording}</em>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||||
import { api, Recording, Room } from './Api';
|
import { api, Recording, Room } from './Api';
|
||||||
import EditRoomDialog from './EditRoomDialog';
|
import EditRoom from './EditRoom';
|
||||||
import RecordingRow from './RecordingRow';
|
import RecordingRow from './RecordingRow';
|
||||||
import EditableValue from './EditableValue';
|
import EditableValue from './EditableValue';
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
</td>
|
</td>
|
||||||
<td><RecordingsNumber recordings={recordings} showRecordings={showRecordings} setShowRecordings={setShowRecordings} /></td>
|
<td><RecordingsNumber recordings={recordings} showRecordings={showRecordings} setShowRecordings={setShowRecordings} /></td>
|
||||||
<td className="edit icon-col">
|
<td className="edit icon-col">
|
||||||
<EditRoomDialog room={props.room} updateProperty={updateRoom} />
|
<EditRoom room={props.room} updateProperty={updateRoom} />
|
||||||
</td>
|
</td>
|
||||||
<td className="remove icon-col">
|
<td className="remove icon-col">
|
||||||
<a className="icon icon-delete icon-visible"
|
<a className="icon icon-delete icon-visible"
|
||||||
|
|
|
@ -14,6 +14,13 @@
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
.icon-group-white {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #a9a9a9;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -51,3 +58,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bbb-form-shareWith {
|
||||||
|
margin-top: -1.5em;
|
||||||
|
}
|
|
@ -4,27 +4,22 @@ import './ShareWith.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
permission: Permission.User | Permission.Moderator;
|
||||||
|
shares: RoomShare[] | undefined;
|
||||||
|
setShares: (shares: RoomShare[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchInput: React.FC<Props> = ({ room }) => {
|
const ShareWith: React.FC<Props> = ({ room, permission, shares: allShares, setShares }) => {
|
||||||
const [search, setSearch] = useState<string>('');
|
const [search, setSearch] = useState<string>('');
|
||||||
const [hasFocus, setFocus] = useState<boolean>(false);
|
const [hasFocus, setFocus] = useState<boolean>(false);
|
||||||
const [recommendations, setRecommendations] = useState<ShareWith>();
|
const [recommendations, setRecommendations] = useState<ShareWith>();
|
||||||
const [searchResults, setSearchResults] = useState<ShareWith>();
|
const [searchResults, setSearchResults] = useState<ShareWith>();
|
||||||
const [shares, setShares] = useState<RoomShare[]>();
|
|
||||||
|
|
||||||
const userShares = shares ? shares.filter(share => share.shareType === ShareType.User).map(share => share.shareWith) : [];
|
const shares = (allShares && permission === Permission.Moderator) ?
|
||||||
const groupShares = shares ? shares.filter(share => share.shareType === ShareType.Group).map(share => share.shareWith) : [];
|
allShares.filter(share => share.permission !== Permission.User) : allShares;
|
||||||
|
|
||||||
useEffect(() => {
|
const sharedUserIds = shares ? shares.filter(share => share.shareType === ShareType.User).map(share => share.shareWith) : [];
|
||||||
api.getRoomShares(room.id).then(roomShares => {
|
const sharedGroupIds = shares ? shares.filter(share => share.shareType === ShareType.Group).map(share => share.shareWith) : [];
|
||||||
setShares(roomShares);
|
|
||||||
}).catch(err => {
|
|
||||||
console.warn('Could not load room shares.', err);
|
|
||||||
|
|
||||||
setShares([]);
|
|
||||||
});
|
|
||||||
}, [room.id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.searchShareWith(search).then(result => {
|
api.searchShareWith(search).then(result => {
|
||||||
|
@ -37,25 +32,40 @@ const SearchInput: React.FC<Props> = ({ room }) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function addRoomShare(shareWith: string, shareType: number, displayName: string) {
|
async function addRoomShare(shareWith: string, shareType: number, displayName: string) {
|
||||||
const roomShare = await api.createRoomShare(room.id, shareType, shareWith, Permission.Moderator);
|
const roomShare = await api.createRoomShare(room.id, shareType, shareWith, permission);
|
||||||
|
|
||||||
roomShare.shareWithDisplayName = displayName;
|
roomShare.shareWithDisplayName = displayName;
|
||||||
|
|
||||||
setShares([...(shares || []), roomShare]);
|
console.log('addRoomShare', allShares, roomShare);
|
||||||
|
|
||||||
|
const newShares = allShares ? [...allShares] : [];
|
||||||
|
const index = newShares.findIndex(share => share.id === roomShare.id);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
newShares[index] = roomShare;
|
||||||
|
} else {
|
||||||
|
newShares.push(roomShare);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('newroomshares', newShares);
|
||||||
|
|
||||||
|
setShares(newShares);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRoomShare(id: number) {
|
async function deleteRoomShare(id: number) {
|
||||||
|
console.log('deleteRoomShare', id);
|
||||||
|
|
||||||
await api.deleteRoomShare(id);
|
await api.deleteRoomShare(id);
|
||||||
|
|
||||||
setShares(shares?.filter(share => share.id !== id));
|
setShares((allShares ? [...allShares] : []).filter(share => share.id !== id));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSearchResults(options: ShareWith) {
|
function renderSearchResults(options: ShareWith) {
|
||||||
return (
|
return (
|
||||||
<ul className="bbb-selection">
|
<ul className="bbb-selection">
|
||||||
{[
|
{[
|
||||||
...options.users.filter(user => !userShares.includes(user.value.shareWith)),
|
...options.users.filter(user => !sharedUserIds.includes(user.value.shareWith)),
|
||||||
...options.groups.filter(group => !groupShares.includes(group.value.shareWith)),
|
...options.groups.filter(group => !sharedGroupIds.includes(group.value.shareWith)),
|
||||||
].map(option => {
|
].map(option => {
|
||||||
return (<li key={option.value.shareWith} onClick={() => addRoomShare(option.value.shareWith, option.value.shareType, option.label)}>
|
return (<li key={option.value.shareWith} onClick={() => addRoomShare(option.value.shareWith, option.value.shareType, option.label)}>
|
||||||
{option.label}{option.value.shareType === ShareType.Group ? ` (${t('bbb', 'Group')})` : ''}
|
{option.label}{option.value.shareType === ShareType.Group ? ` (${t('bbb', 'Group')})` : ''}
|
||||||
|
@ -90,9 +100,10 @@ const SearchInput: React.FC<Props> = ({ room }) => {
|
||||||
<li key={share.id} className="bbb-shareWith__item">
|
<li key={share.id} className="bbb-shareWith__item">
|
||||||
<div className="avatardiv">
|
<div className="avatardiv">
|
||||||
{avatarUrl && <img src={avatarUrl} alt={`Avatar from ${displayName}`} />}
|
{avatarUrl && <img src={avatarUrl} alt={`Avatar from ${displayName}`} />}
|
||||||
|
{share.shareType === ShareType.Group && <span className="icon-group-white"></span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="bbb-shareWith__item__label">
|
<div className="bbb-shareWith__item__label">
|
||||||
<h5>{displayName}{share.shareType === ShareType.Group ? ` (${t('bbb', 'Group')})` : ''}</h5>
|
<h5>{displayName}{(share.permission === Permission.Moderator && permission === Permission.User) ? ` (${t('bbb', 'moderator')})` : ''}</h5>
|
||||||
</div>
|
</div>
|
||||||
{share.id > -1 && <div className="bbb-shareWith__item__action">
|
{share.id > -1 && <div className="bbb-shareWith__item__action">
|
||||||
<a className="icon icon-delete icon-visible"
|
<a className="icon icon-delete icon-visible"
|
||||||
|
@ -126,4 +137,4 @@ const SearchInput: React.FC<Props> = ({ room }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SearchInput;
|
export default ShareWith;
|
Loading…
Reference in New Issue