mirror of https://github.com/sualko/cloud_bbb
parent
a72c877f6b
commit
5debcb6d2a
|
@ -14,6 +14,7 @@ use OCA\BigBlueButton\Event\MeetingStartedEvent;
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
use OCA\BigBlueButton\Permission;
|
use OCA\BigBlueButton\Permission;
|
||||||
use OCA\BigBlueButton\Crypto;
|
use OCA\BigBlueButton\Crypto;
|
||||||
|
use OCA\BigBlueButton\UrlHelper;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
|
@ -40,13 +41,17 @@ class API {
|
||||||
/** @var IL10N */
|
/** @var IL10N */
|
||||||
private $l10n;
|
private $l10n;
|
||||||
|
|
||||||
|
/** @var UrlHelper */
|
||||||
|
private $urlHelper;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IConfig $config,
|
IConfig $config,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
Permission $permission,
|
Permission $permission,
|
||||||
Crypto $crypto,
|
Crypto $crypto,
|
||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
IL10N $l10n
|
IL10N $l10n,
|
||||||
|
UrlHelper $urlHelper
|
||||||
) {
|
) {
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
@ -54,6 +59,7 @@ class API {
|
||||||
$this->crypto = $crypto;
|
$this->crypto = $crypto;
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
|
$this->urlHelper = $urlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getServer() {
|
private function getServer() {
|
||||||
|
@ -132,7 +138,7 @@ class API {
|
||||||
$recordingReadyUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.hook.recordingReady', ['token' => $room->uid, 'mac' => $mac]);
|
$recordingReadyUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.hook.recordingReady', ['token' => $room->uid, 'mac' => $mac]);
|
||||||
$createMeetingParams->setRecordingReadyCallbackUrl($recordingReadyUrl);
|
$createMeetingParams->setRecordingReadyCallbackUrl($recordingReadyUrl);
|
||||||
|
|
||||||
$invitationUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->uid]);
|
$invitationUrl = $this->urlHelper->linkToInvitationAbsolute($room);
|
||||||
$createMeetingParams->setModeratorOnlyMessage($this->l10n->t('To invite someone to the meeting, send them this link: %s', [$invitationUrl]));
|
$createMeetingParams->setModeratorOnlyMessage($this->l10n->t('To invite someone to the meeting, send them this link: %s', [$invitationUrl]));
|
||||||
|
|
||||||
if (!empty($room->welcome)) {
|
if (!empty($room->welcome)) {
|
||||||
|
|
|
@ -5,10 +5,16 @@ namespace OCA\BigBlueButton\Controller;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\IConfig;
|
||||||
|
|
||||||
class PageController extends Controller {
|
class PageController extends Controller {
|
||||||
public function __construct(string $appName, IRequest $request) {
|
/** @var IConfig */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
public function __construct(string $appName, IRequest $request, IConfig $config) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
|
|
||||||
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +22,8 @@ class PageController extends Controller {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*/
|
*/
|
||||||
public function index() {
|
public function index() {
|
||||||
return new TemplateResponse($this->appName, 'manager');
|
return new TemplateResponse($this->appName, 'manager', [
|
||||||
|
'shortener' => $this->config->getAppValue('bbb', 'app.shortener', ''),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Admin implements ISettings {
|
||||||
'api.url' => $this->config->getAppValue('bbb', 'api.url'),
|
'api.url' => $this->config->getAppValue('bbb', 'api.url'),
|
||||||
'api.secret' => $this->config->getAppValue('bbb', 'api.secret'),
|
'api.secret' => $this->config->getAppValue('bbb', 'api.secret'),
|
||||||
'app.navigation' => $this->config->getAppValue('bbb', 'app.navigation') === 'true' ? 'checked' : '',
|
'app.navigation' => $this->config->getAppValue('bbb', 'app.navigation') === 'true' ? 'checked' : '',
|
||||||
|
'app.shortener' => $this->config->getAppValue('bbb', 'app.shortener'),
|
||||||
];
|
];
|
||||||
|
|
||||||
return new TemplateResponse('bbb', 'admin', $parameters);
|
return new TemplateResponse('bbb', 'admin', $parameters);
|
||||||
|
|
|
@ -34,7 +34,10 @@ class Personal implements ISettings {
|
||||||
$warning = $this->l->t('API URL or secret not configured. Please contact your administrator.');
|
$warning = $this->l->t('API URL or secret not configured. Please contact your administrator.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TemplateResponse('bbb', 'manager', ['warning' => $warning]);
|
return new TemplateResponse('bbb', 'manager', [
|
||||||
|
'warning' => $warning,
|
||||||
|
'shortener' => $this->config->getAppValue('bbb', 'app.shortener', ''),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
|
use OCA\BigBlueButton\Db\Room;
|
||||||
|
use OCP\IConfig;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
|
class UrlHelper {
|
||||||
|
/** @var IConfig */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/** @var IURLGenerator */
|
||||||
|
private $urlGenerator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IConfig $config,
|
||||||
|
IURLGenerator $urlGenerator
|
||||||
|
) {
|
||||||
|
$this->config = $config;
|
||||||
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function linkToInvitationAbsolute(Room $room): string {
|
||||||
|
$url = $this->config->getAppValue('bbb', 'app.shortener', '');
|
||||||
|
|
||||||
|
if (empty($url) || strpos($url, 'https://') !== 0 || strpos($url, '{token}') === false) {
|
||||||
|
return $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->getUid()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholders = [];
|
||||||
|
$replacements = [
|
||||||
|
'token' => $room->getUid(),
|
||||||
|
'user' => $room->getUserId(),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($replacements as $placeholder => $parameter) {
|
||||||
|
$placeholders[] = '{' . $placeholder . '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_replace($placeholders, $replacements, $url);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,12 +11,12 @@ script('bbb', 'restrictions');
|
||||||
|
|
||||||
<p><?php p($l->t('Get your API URL and secret by executing "sudo bbb-conf --secret" on your BigBlueButton server.')); ?></p>
|
<p><?php p($l->t('Get your API URL and secret by executing "sudo bbb-conf --secret" on your BigBlueButton server.')); ?></p>
|
||||||
|
|
||||||
<form>
|
<form id="bbb-api">
|
||||||
<input type="url" name="api.url" value="<?php p($_['api.url']); ?>" placeholder="<?php p($l->t('API URL')); ?>" pattern="https://.*" required />
|
<input type="url" name="api.url" value="<?php p($_['api.url']); ?>" placeholder="<?php p($l->t('API URL')); ?>" pattern="https://.*" required />
|
||||||
<input type="password" name="api.secret" value="<?php p($_['api.secret']); ?>" placeholder="<?php p($l->t('API secret')); ?>" autocomplete="new-password" required />
|
<input type="password" name="api.secret" value="<?php p($_['api.secret']); ?>" placeholder="<?php p($l->t('API secret')); ?>" autocomplete="new-password" required />
|
||||||
<input type="submit" value="<?php p($l->t('Save')); ?>" />
|
<input type="submit" value="<?php p($l->t('Save')); ?>" />
|
||||||
|
|
||||||
<div id="bbb-result"></div>
|
<div class="bbb-result"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -24,6 +24,18 @@ script('bbb', 'restrictions');
|
||||||
<label for="bbb-app-navigation"><?php p($l->t('Show room manager in app navigation instead of settings page.')); ?></label>
|
<label for="bbb-app-navigation"><?php p($l->t('Show room manager in app navigation instead of settings page.')); ?></label>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3>URL Shortener</h3>
|
||||||
|
<p><?php p($l->t('If you like to use shorter urls, you can enter a forwarding proxy below.')); ?></p>
|
||||||
|
|
||||||
|
<form id="bbb-shortener">
|
||||||
|
<input type="url" name="app.shortener" value="<?php p($_['app.shortener']); ?>" placeholder="<?php p($l->t('URL shortener')); ?>" pattern="https://.*" />
|
||||||
|
<input type="submit" value="<?php p($l->t('Save')); ?>" />
|
||||||
|
|
||||||
|
<div id="bbb-shortener-example"></div>
|
||||||
|
|
||||||
|
<div class="bbb-result"></div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<h3>Restrictions</h3>
|
<h3>Restrictions</h3>
|
||||||
<div id="bbb-restrictions">
|
<div id="bbb-restrictions">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
script('bbb', 'manager');
|
script('bbb', 'manager');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="bbb-root"></div>
|
<div id="bbb-root" data-shortener="<?php p($_[shortener]); ?>"></div>
|
||||||
|
|
||||||
<?php if (!empty($_['warning'])): ?>
|
<?php if (!empty($_['warning'])): ?>
|
||||||
<div id="bbb-warning">
|
<div id="bbb-warning">
|
||||||
|
|
|
@ -122,6 +122,12 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRoomUrl(room: Room) {
|
public getRoomUrl(room: Room) {
|
||||||
|
const shortener = document.getElementById('bbb-root')?.getAttribute('data-shortener') || '';
|
||||||
|
|
||||||
|
if (shortener) {
|
||||||
|
return shortener.replace(/\{user\}/g, room.userId).replace(/\{token\}/g, room.uid);
|
||||||
|
}
|
||||||
|
|
||||||
return window.location.origin + api.getUrl(`b/${room.uid}`);
|
return window.location.origin + api.getUrl(`b/${room.uid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,14 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-weight: normal;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
padding: 1em 2em;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
#bbb-warning {
|
#bbb-warning {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
background-color: rgb(255, 255, 123);
|
background-color: rgb(255, 255, 123);
|
||||||
|
|
|
@ -187,10 +187,10 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
<>
|
<>
|
||||||
<tr className={showRecordings ? 'selected-row' : ''}>
|
<tr className={showRecordings ? 'selected-row' : ''}>
|
||||||
<td className="start icon-col">
|
<td className="start icon-col">
|
||||||
<a href={api.getUrl(`b/${room.uid}`)} className="icon icon-play icon-visible" target="_blank" rel="noopener noreferrer"></a>
|
<a href={api.getRoomUrl(room)} className="icon icon-play icon-visible" target="_blank" rel="noopener noreferrer"></a>
|
||||||
</td>
|
</td>
|
||||||
<td className="share icon-col">
|
<td className="share icon-col">
|
||||||
<CopyToClipboard text={window.location.origin + api.getUrl(`b/${room.uid}`)}>
|
<CopyToClipboard text={api.getRoomUrl(room)}>
|
||||||
<span className="icon icon-clippy icon-visible copy-to-clipboard" ></span>
|
<span className="icon icon-clippy icon-visible copy-to-clipboard" ></span>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</td>
|
</td>
|
||||||
|
|
95
ts/admin.ts
95
ts/admin.ts
|
@ -34,7 +34,7 @@ $(() => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSettings(url: string, secret: string) {
|
async function saveApiSettings(url: string, secret: string) {
|
||||||
url += url.endsWith('/') ? '' : '/';
|
url += url.endsWith('/') ? '' : '/';
|
||||||
|
|
||||||
await checkServer(url, secret);
|
await checkServer(url, secret);
|
||||||
|
@ -44,19 +44,19 @@ $(() => {
|
||||||
OCP.AppConfig.setValue('bbb', 'api.secret', secret);
|
OCP.AppConfig.setValue('bbb', 'api.secret', secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#bbb-settings form').submit(function (ev) {
|
$('#bbb-api').on('submit', function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
$('#bbb-result').empty();
|
const resultElement = $(this).find('.bbb-result').empty();
|
||||||
|
|
||||||
saveSettings(this['api.url'].value, this['api.secret'].value).then(() => {
|
saveApiSettings(this['api.url'].value, this['api.secret'].value).then(() => {
|
||||||
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$('#bbb-result').empty();
|
resultElement.empty();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
$('#bbb-result').append(successElement);
|
resultElement.append(successElement);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
let message = t('bbb', 'Unexpected error occurred');
|
let message = t('bbb', 'Unexpected error occurred');
|
||||||
|
|
||||||
|
@ -68,11 +68,90 @@ $(() => {
|
||||||
|
|
||||||
const warningElement = generateWarningElement(message);
|
const warningElement = generateWarningElement(message);
|
||||||
|
|
||||||
$('#bbb-result').append(warningElement);
|
resultElement.append(warningElement);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$<HTMLInputElement>('#bbb-settings [name="app.navigation"]').change((ev) => {
|
function generateExampleShortener(shortener: string) {
|
||||||
|
return shortener.replace(/</g, '<').replace(/\{user\}/g, `<strong>${OC.currentUser}</strong>`).replace(/\{token\}/g, '<strong>your_room_id</strong>');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveAppSettings(shortener: string) {
|
||||||
|
await checkPasswordConfirmation();
|
||||||
|
|
||||||
|
if (shortener.indexOf('https://') !== 0) {
|
||||||
|
throw 'https';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortener.indexOf('{token}') < 0) {
|
||||||
|
throw 'token';
|
||||||
|
}
|
||||||
|
|
||||||
|
OCP.AppConfig.setValue('bbb', 'app.shortener', shortener);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bbb-shortener').on('submit', function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
const resultElement = $(this).find('.bbb-result').empty();
|
||||||
|
|
||||||
|
saveAppSettings(this['app.shortener'].value).then(() => {
|
||||||
|
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resultElement.empty();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
resultElement.append(successElement);
|
||||||
|
}).catch(err => {
|
||||||
|
let message = t('bbb', 'Unexpected error occurred');
|
||||||
|
|
||||||
|
if (err === 'https') {
|
||||||
|
message = t('bbb', 'URL has to start with https');
|
||||||
|
} else if (err === 'token') {
|
||||||
|
message = t('bbb', 'URL has to contain the {token} placeholder');
|
||||||
|
}
|
||||||
|
|
||||||
|
const warningElement = generateWarningElement(message);
|
||||||
|
|
||||||
|
console.warn('Could not save app settings', err);
|
||||||
|
|
||||||
|
resultElement.append(warningElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$<HTMLInputElement>('#bbb-shortener [name="app.shortener"]').on('keyup', (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
const {value} = ev.target;
|
||||||
|
|
||||||
|
if (!value || value.indexOf('https://') !== 0 || value.indexOf('{token}') < 0) {
|
||||||
|
$('#bbb-shortener-example').text(t('bbb', 'URL has to start with https:// and contain {token}. Additionally the {user} placeholder can be used.'));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = window.location.origin + OC.generateUrl('apps/bbb/b/$1');
|
||||||
|
const url = (new URL(value));
|
||||||
|
const rewritePath = '^' + url.pathname.replace(/^\//, '').replace(/%7Buser%7D/g, '.+').replace(/%7Btoken%7D/g, '(.+)');
|
||||||
|
|
||||||
|
$('#bbb-shortener-example').html(`<p>${generateExampleShortener(value)}</p>
|
||||||
|
<details>
|
||||||
|
<summary>${t('bbb', 'Example configuration for Apache and Nginx')}</summary>
|
||||||
|
<pre>#Apache with mod_rewrite
|
||||||
|
ServerName ${url.hostname}
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteRule "${rewritePath}" "${target}" [R=307,L]
|
||||||
|
|
||||||
|
#Nginx config
|
||||||
|
server_name ${url.hostname};
|
||||||
|
rewrite ${rewritePath} ${target} last;
|
||||||
|
return 307;</pre></details>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
$('#bbb-shortener [name="app.shortener"]').trigger('keyup');
|
||||||
|
|
||||||
|
$<HTMLInputElement>('#bbb-settings [name="app.navigation"]').on('change', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
console.log('checkbox changed to', ev.target.checked);
|
console.log('checkbox changed to', ev.target.checked);
|
||||||
|
|
Loading…
Reference in New Issue