feat: add option to insert document

to running meeting. Currently only available in debug mode,
because BBB PHP API needs to be injected manually.
pull/201/head
sualko 2022-03-17 14:59:47 +01:00
parent 65b499d7a3
commit d5b9c82e1d
8 changed files with 128 additions and 6 deletions

View File

@ -8,6 +8,8 @@ return [
], ],
'routes' => [ 'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'server#isRunning', 'url' => '/server/{roomUid}/isRunning', 'verb' => 'GET'],
['name' => 'server#insertDocument', 'url' => '/server/{roomUid}/insertDocument', 'verb' => 'POST'],
['name' => 'server#records', 'url' => '/server/{roomUid}/records', 'verb' => 'GET'], ['name' => 'server#records', 'url' => '/server/{roomUid}/records', 'verb' => 'GET'],
['name' => 'server#check', 'url' => '/server/check', 'verb' => 'POST'], ['name' => 'server#check', 'url' => '/server/check', 'verb' => 'POST'],
['name' => 'server#version', 'url' => '/server/version', 'verb' => 'GET'], ['name' => 'server#version', 'url' => '/server/version', 'verb' => 'GET'],

View File

@ -7,6 +7,7 @@ use BigBlueButton\Core\Record;
use BigBlueButton\Parameters\CreateMeetingParameters; use BigBlueButton\Parameters\CreateMeetingParameters;
use BigBlueButton\Parameters\DeleteRecordingsParameters; use BigBlueButton\Parameters\DeleteRecordingsParameters;
use BigBlueButton\Parameters\GetRecordingsParameters; use BigBlueButton\Parameters\GetRecordingsParameters;
use BigBlueButton\Parameters\InsertDocumentParameters;
use BigBlueButton\Parameters\IsMeetingRunningParameters; use BigBlueButton\Parameters\IsMeetingRunningParameters;
use BigBlueButton\Parameters\JoinMeetingParameters; use BigBlueButton\Parameters\JoinMeetingParameters;
use OCA\BigBlueButton\AppInfo\Application; use OCA\BigBlueButton\AppInfo\Application;
@ -320,4 +321,14 @@ class API {
return $response->success() && $response->isRunning(); return $response->success() && $response->isRunning();
} }
public function insertDocument(Room $room, string $url, string $filename): bool {
$insertDocumentParams = new InsertDocumentParameters($room->getUid());
$insertDocumentParams->addPresentation($url, $filename, null, null);
$response = $this->getServer()->insertDocument($insertDocumentParams);
return $response->success();
}
} }

View File

@ -40,6 +40,44 @@ class ServerController extends Controller {
$this->userId = $UserId; $this->userId = $UserId;
} }
/**
* @NoAdminRequired
*/
public function isRunning(string $roomUid): DataResponse {
$room = $this->service->findByUid($roomUid);
if ($room === null) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if (!$this->permission->isUser($room, $this->userId)) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$isRunning = $this->server->isRunning($room);
return new DataResponse($isRunning);
}
/**
* @NoAdminRequired
*/
public function insertDocument(string $roomUid, string $url, string $filename): DataResponse {
$room = $this->service->findByUid($roomUid);
if ($room === null) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if (!$this->permission->isModerator($room, $this->userId)) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$success = $this->server->insertDocument($room, $url, $filename);
return new DataResponse($success);
}
/** /**
* @NoAdminRequired * @NoAdminRequired
*/ */

View File

@ -38,6 +38,7 @@
"@commitlint/config-conventional": "^16.2.1", "@commitlint/config-conventional": "^16.2.1",
"@commitlint/travis-cli": "^16.2.3", "@commitlint/travis-cli": "^16.2.3",
"@nextcloud/axios": "^1.3.2", "@nextcloud/axios": "^1.3.2",
"@nextcloud/dialogs": "^3.1.2",
"@nextcloud/router": "^2.0.0", "@nextcloud/router": "^2.0.0",
"@octokit/rest": "^18.0.4", "@octokit/rest": "^18.0.4",
"archiver": "^5.0.0", "archiver": "^5.0.0",

View File

@ -133,6 +133,18 @@ class Api {
return response.data; return response.data;
} }
public async isRunning(uid: string): Promise<boolean> {
const response = await axios.get(this.getUrl(`server/${uid}/isRunning`));
return response.data;
}
public async insertDocument(uid: string, url: string, filename: string): Promise<boolean> {
const response = await axios.post(this.getUrl(`server/${uid}/insertDocument`), { url, filename });
return response.data;
}
public getRoomUrl(room: Room, forModerator = false) { public getRoomUrl(room: Room, forModerator = false) {
const shortener = document.getElementById('bbb-root')?.getAttribute('data-shortener') || ''; const shortener = document.getElementById('bbb-root')?.getAttribute('data-shortener') || '';
const token = (forModerator && room.moderatorToken) ? `${room.uid}/${room.moderatorToken}` : room.uid; const token = (forModerator && room.moderatorToken) ? `${room.uid}/${room.moderatorToken}` : room.uid;
@ -258,9 +270,9 @@ class Api {
groups: [], groups: [],
circles: [], circles: [],
exact: { exact: {
users: response.data.ocs.data.exact.users, users: response.data.ocs.data.exact.users,
groups: response.data.ocs.data.exact.groups, groups: response.data.ocs.data.exact.groups,
circles: response.data.ocs.data.exact.circles || [], circles: response.data.ocs.data.exact.circles || [],
}, },
}; };
} }

2
ts/Nextcloud.d.ts vendored
View File

@ -73,6 +73,8 @@ declare namespace OC {
const PERMISSION_SHARE = 16; const PERMISSION_SHARE = 16;
const PERMISSION_ALL = 31; const PERMISSION_ALL = 31;
const debug: boolean;
const currentUser: string; const currentUser: string;
function getCurrentUser(): {uid: string; displayName: string} function getCurrentUser(): {uid: string; displayName: string}

View File

@ -1,5 +1,7 @@
import axios from '@nextcloud/axios'; import axios from '@nextcloud/axios';
import { generateOcsUrl, generateUrl } from '@nextcloud/router'; import { generateOcsUrl, generateUrl } from '@nextcloud/router';
import { showSuccess, showWarning, showError } from '@nextcloud/dialogs';
import '@nextcloud/dialogs/styles/toast';
import { api } from './Common/Api'; import { api } from './Common/Api';
type OC_Dialogs_Message = (content: string, title: string, dialogType: 'notice' | 'alert' | 'warn' | 'none', buttons?: number, callback?: () => void, modal?: boolean, allowHtml?: boolean) => Promise<void>; type OC_Dialogs_Message = (content: string, title: string, dialogType: 'notice' | 'alert' | 'warn' | 'none', buttons?: number, callback?: () => void, modal?: boolean, allowHtml?: boolean) => Promise<void>;
@ -38,8 +40,7 @@ async function createDirectShare(fileId: number): Promise<string> {
return createResponse.data?.ocs?.data?.url; return createResponse.data?.ocs?.data?.url;
} }
async function share(fileId: number, filename: string, roomUid: string) { async function createRoomWithFile(shareUrl: string, filename: string, roomUid: string) {
const shareUrl = await createDirectShare(fileId);
const joinUrl = generateUrl('/apps/bbb/b/{uid}?u={url}&filename={filename}', { const joinUrl = generateUrl('/apps/bbb/b/{uid}?u={url}&filename={filename}', {
uid: roomUid, uid: roomUid,
url: shareUrl, url: shareUrl,
@ -49,6 +50,31 @@ async function share(fileId: number, filename: string, roomUid: string) {
window.open(joinUrl, '_blank', 'noopener,noreferrer'); window.open(joinUrl, '_blank', 'noopener,noreferrer');
} }
function insertDocumentToRoom(shareUrl: string, filename: string, roomUid: string) {
return api.insertDocument(roomUid, shareUrl, filename);
}
async function sendFile(fileId: number, filename: string, roomUid: string) {
const shareUrl = await createDirectShare(fileId);
const isRunning = await api.isRunning(roomUid);
if (isRunning) {
try {
const success = await insertDocumentToRoom(shareUrl, filename, roomUid);
if (success) {
showSuccess(t('bbb', 'The file "{filename}" was uploaded to your room.', { filename }));
} else {
showWarning(t('bbb', 'The file "{filename}" could not be uploaded to your room.', { filename }));
}
} catch {
showError(t('bbb', 'The file "{filename}" could not be uploaded to your room. Maybe your BigBlueButton server does not support this action.', { filename }));
}
} else {
createRoomWithFile(shareUrl, filename, roomUid);
}
}
async function openDialog(fileId: number, filename: string) { async function openDialog(fileId: number, filename: string) {
const initContent = '<div id="bbb-file-action"><span className="icon icon-loading-small icon-visible"></span></div>'; const initContent = '<div id="bbb-file-action"><span className="icon icon-loading-small icon-visible"></span></div>';
const title = t('bbb', 'Send file to BBB'); const title = t('bbb', 'Send file to BBB');
@ -65,13 +91,16 @@ async function openDialog(fileId: number, filename: string) {
const row = $('<tr>'); const row = $('<tr>');
const button = $('<button>'); const button = $('<button>');
if (!OC.debug) {
button.prop('disabled', room.running);
}
button.text(room.running ? t('bbb', 'Send to') : t('bbb', 'Start with')); button.text(room.running ? t('bbb', 'Send to') : t('bbb', 'Start with'));
button.addClass('primary'); button.addClass('primary');
button.attr('type', 'button'); button.attr('type', 'button');
button.on('click', (ev) => { button.on('click', (ev) => {
ev.preventDefault(); ev.preventDefault();
share(fileId, filename, room.uid); sendFile(fileId, filename, room.uid);
container.parents('.oc-dialog').find('.oc-dialog-close').trigger('click'); container.parents('.oc-dialog').find('.oc-dialog-close').trigger('click');
}); });

View File

@ -1330,6 +1330,16 @@
resolved "https://registry.yarnpkg.com/@nextcloud/browserslist-config/-/browserslist-config-2.2.0.tgz#85c2b9363b4aa98ea534576b9f9094cc0afa8f78" resolved "https://registry.yarnpkg.com/@nextcloud/browserslist-config/-/browserslist-config-2.2.0.tgz#85c2b9363b4aa98ea534576b9f9094cc0afa8f78"
integrity sha512-kC42RQW5rZjZZsRaEjVlIQpp6aW/yxm+zZdETnrRQnUzcPwBgF4wO4makfGT63Ckd+LkgUW+geesPiPRqxFVew== integrity sha512-kC42RQW5rZjZZsRaEjVlIQpp6aW/yxm+zZdETnrRQnUzcPwBgF4wO4makfGT63Ckd+LkgUW+geesPiPRqxFVew==
"@nextcloud/dialogs@^3.1.2":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@nextcloud/dialogs/-/dialogs-3.1.2.tgz#84c6b9dd13f757863a2d95215e00a161d6bb654a"
integrity sha512-hVgpr/CF0F+cE7tRZHJDVpB1S05K/pDcUMrfDpoxMKhux5SXlpwLXUaWM7iAbHEKYm6ArWdpUyhxBTTAo9yrvg==
dependencies:
"@nextcloud/l10n" "^1.3.0"
"@nextcloud/typings" "^1.0.0"
core-js "^3.6.4"
toastify-js "^1.10.0"
"@nextcloud/eslint-plugin@^2.0.0": "@nextcloud/eslint-plugin@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/@nextcloud/eslint-plugin/-/eslint-plugin-2.0.0.tgz#55336973752af669954b0cf0c8887503e8494ba6" resolved "https://registry.yarnpkg.com/@nextcloud/eslint-plugin/-/eslint-plugin-2.0.0.tgz#55336973752af669954b0cf0c8887503e8494ba6"
@ -1376,6 +1386,13 @@
dependencies: dependencies:
"@types/jquery" "2.0.54" "@types/jquery" "2.0.54"
"@nextcloud/typings@^1.0.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@nextcloud/typings/-/typings-1.3.0.tgz#8898d3a31c10a31b409c6815560285f047f699cc"
integrity sha512-lxAWXobayeaAnydCWKMljJXDLXECimUf3HbTjnrjL7pslPyGyH70LHj4NZ5BmGbKeU2yjF2FTXIGji10lOryiQ==
dependencies:
"@types/jquery" "2.0.57"
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
version "2.1.8-no-fsevents.3" version "2.1.8-no-fsevents.3"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
@ -1606,6 +1623,11 @@
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.54.tgz#d7999245f77c3fab5d84e7d32b8a6c20bfd1f072" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.54.tgz#d7999245f77c3fab5d84e7d32b8a6c20bfd1f072"
integrity sha512-D/PomKwNkDfSKD13DEVQT/pq2TUjN54c6uB341fEZanIzkjfGe7UaFuuaLZbpEiS5j7Wk2MUHAZqZIoECw29lg== integrity sha512-D/PomKwNkDfSKD13DEVQT/pq2TUjN54c6uB341fEZanIzkjfGe7UaFuuaLZbpEiS5j7Wk2MUHAZqZIoECw29lg==
"@types/jquery@2.0.57":
version "2.0.57"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.57.tgz#df4241708df642021c5c715de64207371240c6e5"
integrity sha512-QUJ5wVL8iTZofgZjCfVnHxcMqqPPLfVfEKe8rfksMdmSmqEenpcpEBQO45VSSfng/tunwoLF+3I8rzEzVhYNLQ==
"@types/jquery@^3.3.35": "@types/jquery@^3.3.35":
version "3.5.14" version "3.5.14"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.14.tgz#ac8e11ee591e94d4d58da602cb3a5a8320dee577" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.14.tgz#ac8e11ee591e94d4d58da602cb3a5a8320dee577"
@ -6759,6 +6781,11 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
toastify-js@^1.10.0:
version "1.11.2"
resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.11.2.tgz#5c66cb05cee30b09e5bd8961ab809e41a3ce716c"
integrity sha512-bMBNKhZLPX/sDhpwM7KHIRUTtqCylQeoZDiEWy5zE7iDUJ92XmP8AKgDAp9rXx6pR5GXGFtQHHoH62toahbHgQ==
toggle-selection@^1.0.6: toggle-selection@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"