feat: add new join options

- disable listen only mode
- skip audio check and video preview on first join
- clean layout without presentation, chat area and user list
pull/149/head
sualko 2021-04-19 14:47:15 +02:00
parent c39ab49e76
commit 958af34244
7 changed files with 161 additions and 23 deletions

View File

@ -89,6 +89,15 @@ class API {
$joinMeetingParams->setRedirect(true); $joinMeetingParams->setRedirect(true);
$joinMeetingParams->setGuest($uid === null); $joinMeetingParams->setGuest($uid === null);
$joinMeetingParams->addUserData('bbb_listen_only_mode', $room->getListenOnly());
$joinMeetingParams->addUserData('bbb_skip_check_audio_on_first_join', !$room->getMediaCheck()); // 2.3
$joinMeetingParams->addUserData('bbb_skip_video_preview_on_first_join', !$room->getMediaCheck()); // 2.3
$joinMeetingParams->addUserData('bbb_auto_swap_layout', $room->getCleanLayout());
$joinMeetingParams->addUserData('bbb_show_participants_on_login', !$room->getCleanLayout());
$joinMeetingParams->addUserData('bbb_show_public_chat_on_login', !$room->getCleanLayout());
if ($this->config->getAppValue('bbb', 'join.theme') === 'true') { if ($this->config->getAppValue('bbb', 'join.theme') === 'true') {
$primaryColor = $this->defaults->getColorPrimary(); $primaryColor = $this->defaults->getColorPrimary();
$textColor = $this->defaults->getTextColorPrimary(); $textColor = $this->defaults->getTextColorPrimary();

View File

@ -115,7 +115,10 @@ class RoomController extends Controller {
string $access, string $access,
bool $everyoneIsModerator, bool $everyoneIsModerator,
bool $requireModerator, bool $requireModerator,
?string $moderatorToken ?string $moderatorToken,
bool $listenOnly,
bool $mediaCheck,
bool $cleanLayout
): DataResponse { ): DataResponse {
$room = $this->service->find($id); $room = $this->service->find($id);
@ -138,8 +141,8 @@ class RoomController extends Controller {
return new DataResponse(['message' => 'Access type not allowed.'], Http::STATUS_BAD_REQUEST); return new DataResponse(['message' => 'Access type not allowed.'], Http::STATUS_BAD_REQUEST);
} }
return $this->handleNotFound(function () use ($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $requireModerator, $moderatorToken) { return $this->handleNotFound(function () use ($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $requireModerator, $moderatorToken, $listenOnly, $mediaCheck, $cleanLayout) {
return $this->service->update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $requireModerator, $moderatorToken); return $this->service->update($id, $name, $welcome, $maxParticipants, $record, $access, $everyoneIsModerator, $requireModerator, $moderatorToken, $listenOnly, $mediaCheck, $cleanLayout);
}); });
} }

View File

@ -21,6 +21,9 @@ use OCP\AppFramework\Db\Entity;
* @method bool getRequireModerator() * @method bool getRequireModerator()
* @method bool getEveryoneIsModerator() * @method bool getEveryoneIsModerator()
* @method string getModeratorToken() * @method string getModeratorToken()
* @method bool getListenOnly()
* @method bool getMediaCheck()
* @method bool getCleanLayout()
* @method void setUid(string $uid) * @method void setUid(string $uid)
* @method void setName(string $name) * @method void setName(string $name)
* @method void setAttendeePassword(string $pw) * @method void setAttendeePassword(string $pw)
@ -34,6 +37,9 @@ use OCP\AppFramework\Db\Entity;
* @method void setEveryoneIsModerator(bool $everyone) * @method void setEveryoneIsModerator(bool $everyone)
* @method void setRequireModerator(bool $require) * @method void setRequireModerator(bool $require)
* @method void setModeratorToken(string $moderatorToken) * @method void setModeratorToken(string $moderatorToken)
* @method void setListenOnly(bool $listenOnly)
* @method void setMediaCheck(bool $mediaCheck)
* @method void setCleanLayout(bool $cleanLayout)
*/ */
class Room extends Entity implements JsonSerializable { class Room extends Entity implements JsonSerializable {
public const ACCESS_PUBLIC = 'public'; public const ACCESS_PUBLIC = 'public';
@ -58,6 +64,9 @@ class Room extends Entity implements JsonSerializable {
public $requireModerator = false; public $requireModerator = false;
public $shared = false; public $shared = false;
public $moderatorToken; public $moderatorToken;
public $listenOnly;
public $mediaCheck;
public $cleanLayout;
public function __construct() { public function __construct() {
$this->addType('maxParticipants', 'integer'); $this->addType('maxParticipants', 'integer');
@ -65,6 +74,9 @@ class Room extends Entity implements JsonSerializable {
$this->addType('everyoneIsModerator', 'boolean'); $this->addType('everyoneIsModerator', 'boolean');
$this->addType('requireModerator', 'boolean'); $this->addType('requireModerator', 'boolean');
$this->addType('shared', 'boolean'); $this->addType('shared', 'boolean');
$this->addType('listenOnly', 'boolean');
$this->addType('mediaCheck', 'boolean');
$this->addType('cleanLayout', 'boolean');
} }
public function jsonSerialize(): array { public function jsonSerialize(): array {
@ -82,6 +94,9 @@ class Room extends Entity implements JsonSerializable {
'requireModerator' => boolval($this->requireModerator), 'requireModerator' => boolval($this->requireModerator),
'shared' => boolval($this->shared), 'shared' => boolval($this->shared),
'moderatorToken' => $this->moderatorToken, 'moderatorToken' => $this->moderatorToken,
'listenOnly' => boolval($this->listenOnly),
'mediaCheck' => boolval($this->mediaCheck),
'cleanLayout' => boolval($this->cleanLayout),
]; ];
} }
} }

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace OCA\BigBlueButton\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version000000Date20210419132000 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) {
$schema = $schemaClosure();
if ($schema->hasTable('bbb_rooms')) {
$table = $schema->getTable('bbb_rooms');
if (!$table->hasColumn('listen_only')) {
$table->addColumn('listen_only', 'boolean', [
'notnull' => true,
'default' => true
]);
}
if (!$table->hasColumn('media_check')) {
$table->addColumn('media_check', 'boolean', [
'notnull' => true,
'default' => true
]);
}
if (!$table->hasColumn('clean_layout')) {
$table->addColumn('clean_layout', 'boolean', [
'notnull' => true,
'default' => false
]);
}
return $schema;
}
return null;
}
}

View File

@ -102,7 +102,19 @@ class RoomService {
* *
* @return \OCP\AppFramework\Db\Entity|null * @return \OCP\AppFramework\Db\Entity|null
*/ */
public function update(int $id, string $name, string $welcome, int $maxParticipants, bool $record, string $access, bool $everyoneIsModerator, bool $requireModerator, ?string $moderatorToken) { public function update(
int $id,
string $name,
string $welcome,
int $maxParticipants,
bool $record,
string $access,
bool $everyoneIsModerator,
bool $requireModerator,
?string $moderatorToken,
bool $listenOnly,
bool $mediaCheck,
bool $cleanLayout) {
try { try {
$room = $this->mapper->find($id); $room = $this->mapper->find($id);
@ -121,6 +133,9 @@ class RoomService {
$room->setAccess($access); $room->setAccess($access);
$room->setEveryoneIsModerator($everyoneIsModerator); $room->setEveryoneIsModerator($everyoneIsModerator);
$room->setRequireModerator($requireModerator); $room->setRequireModerator($requireModerator);
$room->setListenOnly($listenOnly);
$room->setMediaCheck($mediaCheck);
$room->setCleanLayout($cleanLayout);
return $this->mapper->update($room); return $this->mapper->update($room);
} catch (Exception $e) { } catch (Exception $e) {

View File

@ -39,6 +39,9 @@ export interface Room {
requireModerator: boolean; requireModerator: boolean;
shared: boolean; shared: boolean;
moderatorToken: string; moderatorToken: string;
listenOnly: boolean,
mediaCheck: boolean,
cleanLayout: boolean,
} }
export interface RoomShare { export interface RoomShare {

View File

@ -17,6 +17,9 @@ const descriptions: { [key: string]: string } = {
requireModerator: t('bbb', 'If enabled, normal users have to wait until a moderator is in the room.'), requireModerator: t('bbb', 'If enabled, normal users have to wait until a moderator is in the room.'),
moderatorToken: t('bbb', 'If enabled, a moderator URL is generated which allows access with moderator permission.'), moderatorToken: t('bbb', 'If enabled, a moderator URL is generated which allows access with moderator permission.'),
internalRestrictedShareWith: t('bbb', 'Only selected users and groups are allowed to access the room.'), internalRestrictedShareWith: t('bbb', 'Only selected users and groups are allowed to access the room.'),
listenOnly: t('bbb', 'If disabled, a microphone is needed to join the conference.'),
mediaCheck: t('bbb', 'If enabled, the user has not to perform an echo call and webcam preview on the first join (available since BBB server 2.3).'),
cleanLayout: t('bbb', 'If enabled, the user list, chat area and presentation are hidden by default.'),
}; };
const LOGO_QR = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGnSURBVFiF7de9TxRBHMbxzxG5BonRBOGsVRJfIvGFPwFKX0tjJGqsrfwvCC0YtbJSQyT4J0hB1BhtjZFCI4FoqTRCsbO43g24e+5Q3ZNsZm9+z8zzvZns7Rw9/a0jeIx1bNZ8rYe5WzuFt7CSILj9WsFwHtooADzA7XD/DG/CgDrUwHlcDZ/ncLfdtBoCn9cUGtN8yPiWd/QVikOhfZcQ4G1oD8cA8u2oa9ljyufe3vq+HYx7ph7Avv8YO4Rx2b4uy35oKqubFWhiBl+wiJf4imn0V52smxWYxc22vn7cwwHcqjJZ1RUYi4QXNYUzKQEm/1FvYCIlwEAJz/6UAB9KeN6nBFjAp13qH2VPRjKADdkr9Uek9h3XgicZwGk8wcFI7VConUoFMIZXOLGL5ySWVHgUywI08RSDJbyDwdusE+AGjpb0wjFcrxPgSoXwXJerAnScVgo63gXAaKSv49RVBFgL7dnIwN9dAMR0LrSreUfxbfgCd3BJdix/7Q/pBn5WDPuF++G+gQu4WMjq0Ii9+WPyWeFU3K4WHsm2o+7gNTwMX7SnbW0BScCZl0uGVe8AAAAASUVORK5CYII='; const LOGO_QR = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGnSURBVFiF7de9TxRBHMbxzxG5BonRBOGsVRJfIvGFPwFKX0tjJGqsrfwvCC0YtbJSQyT4J0hB1BhtjZFCI4FoqTRCsbO43g24e+5Q3ZNsZm9+z8zzvZns7Rw9/a0jeIx1bNZ8rYe5WzuFt7CSILj9WsFwHtooADzA7XD/DG/CgDrUwHlcDZ/ncLfdtBoCn9cUGtN8yPiWd/QVikOhfZcQ4G1oD8cA8u2oa9ljyufe3vq+HYx7ph7Avv8YO4Rx2b4uy35oKqubFWhiBl+wiJf4imn0V52smxWYxc22vn7cwwHcqjJZ1RUYi4QXNYUzKQEm/1FvYCIlwEAJz/6UAB9KeN6nBFjAp13qH2VPRjKADdkr9Uek9h3XgicZwGk8wcFI7VConUoFMIZXOLGL5ySWVHgUywI08RSDJbyDwdusE+AGjpb0wjFcrxPgSoXwXJerAnScVgo63gXAaKSv49RVBFgL7dnIwN9dAMR0LrSreUfxbfgCd3BJdix/7Q/pBn5WDPuF++G+gQu4WMjq0Ii9+WPyWeFU3K4WHsm2o+7gNTwMX7SnbW0BScCZl0uGVe8AAAAASUVORK5CYII=';
@ -153,6 +156,7 @@ const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, op
<em>{descriptions.moderatorToken}</em> <em>{descriptions.moderatorToken}</em>
</div> </div>
<div className="bbb-form-element">
<h3>{t('bbb', 'Miscellaneous')}</h3> <h3>{t('bbb', 'Miscellaneous')}</h3>
<div> <div>
<div> <div>
@ -177,6 +181,40 @@ const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, op
</div> </div>
<p><em>{descriptions.requireModerator}</em></p> <p><em>{descriptions.requireModerator}</em></p>
</div> </div>
<div>
<div>
<input id={`bbb-listenOnly-${room.id}`}
type="checkbox"
className="checkbox"
checked={room.listenOnly}
onChange={(event) => updateProperty('listenOnly', event.target.checked)} />
<label htmlFor={`bbb-listenOnly-${room.id}`}>{t('bbb', 'Listen only option')}</label>
</div>
<p><em>{descriptions.listenOnly}</em></p>
</div>
<div>
<div>
<input id={`bbb-mediaCheck-${room.id}`}
type="checkbox"
className="checkbox"
checked={!room.mediaCheck}
onChange={(event) => updateProperty('mediaCheck', !event.target.checked)} />
<label htmlFor={`bbb-mediaCheck-${room.id}`}>{t('bbb', 'Skip media check before usage')}</label>
</div>
<p><em>{descriptions.mediaCheck}</em></p>
</div>
<div>
<div>
<input id={`bbb-cleanLayout-${room.id}`}
type="checkbox"
className="checkbox"
checked={room.cleanLayout}
onChange={(event) => updateProperty('cleanLayout', event.target.checked)} />
<label htmlFor={`bbb-cleanLayout-${room.id}`}>{t('bbb', 'Clean layout')}</label>
</div>
<p><em>{descriptions.cleanLayout}</em></p>
</div>
</div>
</Dialog> </Dialog>
); );
}; };