mirror of https://github.com/sualko/cloud_bbb
Compare commits
14 Commits
42f92dc977
...
c1e42a836e
Author | SHA1 | Date |
---|---|---|
|
c1e42a836e | |
|
525d3fa525 | |
|
9a974fcd47 | |
|
dade62576e | |
|
4cbb88cb64 | |
|
0f4c1682f5 | |
|
96a3a4527d | |
|
02168650a4 | |
|
a3b01dbc64 | |
|
a28ab208ef | |
|
c926cc63d7 | |
|
b7aee468b2 | |
|
48f0995b21 | |
|
07611b2a7b |
|
@ -28,7 +28,8 @@ module.exports = {
|
|||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'array-bracket-newline': ['error', 'consistent'],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
'indent': ['warn', 'tab'],
|
||||
semi: ["error", "always"],
|
||||
indent: ['warn', 'tab'],
|
||||
semi: ['error', 'always'],
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
},
|
||||
}
|
19
README.md
19
README.md
|
@ -69,15 +69,16 @@ used configuration keys in the list below. Please beware that there will be no
|
|||
check if those values are correct. Therefore this is not the recommended way.
|
||||
The syntax to set all settings is `occ config:app:set bbb KEY --value "VALUE"`.
|
||||
|
||||
Key | Description
|
||||
--------------------- | ------------------------------------------------------------------------------------
|
||||
`app.navigation` | Set to `true` to show navigation entry
|
||||
`app.navigation.name` | Defines the navigation label. Default "BigBlueButton".
|
||||
`api.url` | URL to your BBB server. Should start with `https://`
|
||||
`api.secret` | Secret of your BBB server
|
||||
`app.shortener` | Value of your shortener service. Should start with `https://` and contain `{token}`.
|
||||
`avatar.path` | Absolute path to an optional avatar cache directory.
|
||||
`avatar.url` | URL which serves `avatar.path` to be used as avatar cache.
|
||||
Key | Description
|
||||
--------------------------------- | ------------------------------------------------------------------------------------
|
||||
`app.navigation` | Set to `true` to show navigation entry
|
||||
`app.navigation.name` | Defines the navigation label. Default "BigBlueButton".
|
||||
`api.url` | URL to your BBB server. Should start with `https://`
|
||||
`api.secret` | Secret of your BBB server
|
||||
`api.meta_analytics-callback-url` | URL which gets called after meetings ends to generate statistics. See [bbb-analytics](https://github.com/betagouv/bbb-analytics).
|
||||
`app.shortener` | Value of your shortener service. Should start with `https://` and contain `{token}`.
|
||||
`avatar.path` | Absolute path to an optional avatar cache directory.
|
||||
`avatar.url` | URL which serves `avatar.path` to be used as avatar cache.
|
||||
|
||||
### Avatar cache (v2.2+)
|
||||
The generation of avatars puts a high load on your Nextcloud instance, since the
|
||||
|
|
|
@ -106,7 +106,9 @@ class API {
|
|||
$joinMeetingParams->setCreateTime(sprintf("%.0f", $creationTime));
|
||||
$joinMeetingParams->setJoinViaHtml5(true);
|
||||
$joinMeetingParams->setRedirect(true);
|
||||
$joinMeetingParams->setGuest($uid === null);
|
||||
|
||||
// set the guest parameter for everyone but moderators to send all users to the waiting room if setting is selected
|
||||
$joinMeetingParams->setGuest((($room->access === Room::ACCESS_WAITING_ROOM_ALL) && !$isModerator) || $uid === null);
|
||||
|
||||
$joinMeetingParams->addUserData('bbb_listen_only_mode', $room->getListenOnly());
|
||||
|
||||
|
@ -175,6 +177,13 @@ class API {
|
|||
$createMeetingParams->addMeta('bbb-origin', \method_exists($this->defaults, 'getProductName') ? $this->defaults->getProductName() : 'Nextcloud');
|
||||
$createMeetingParams->addMeta('bbb-origin-server-name', $this->request->getServerHost());
|
||||
|
||||
$analyticsCallbackUrl = $this->config->getAppValue('bbb', 'api.meta_analytics-callback-url');
|
||||
if (!empty($analyticsCallbackUrl)) {
|
||||
// For more details: https://github.com/bigbluebutton/bigbluebutton/blob/develop/record-and-playback/core/scripts/post_events/post_events_analytics_callback.rb
|
||||
$createMeetingParams->addMeta('analytics-callback-url', $analyticsCallbackUrl);
|
||||
$createMeetingParams->setMeetingKeepEvents(true);
|
||||
}
|
||||
|
||||
$mac = $this->crypto->calculateHMAC($room->uid);
|
||||
|
||||
$endMeetingUrl = $this->urlGenerator->linkToRouteAbsolute('bbb.hook.meetingEnded', ['token' => $room->uid, 'mac' => $mac]);
|
||||
|
@ -199,7 +208,7 @@ class API {
|
|||
$createMeetingParams->addPresentation($presentation->getUrl(), null, $presentation->getFilename());
|
||||
}
|
||||
|
||||
if ($room->access === Room::ACCESS_WAITING_ROOM) {
|
||||
if ($room->access === Room::ACCESS_WAITING_ROOM || $room->access === Room::ACCESS_WAITING_ROOM_ALL) {
|
||||
$createMeetingParams->setGuestPolicyAskModerator();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,11 @@ class Room extends Entity implements JsonSerializable {
|
|||
public const ACCESS_PUBLIC = 'public';
|
||||
public const ACCESS_PASSWORD = 'password';
|
||||
public const ACCESS_WAITING_ROOM = 'waiting_room';
|
||||
public const ACCESS_WAITING_ROOM_ALL = 'waiting_room_all';
|
||||
public const ACCESS_INTERNAL = 'internal';
|
||||
public const ACCESS_INTERNAL_RESTRICTED = 'internal_restricted';
|
||||
|
||||
public const ACCESS = [self::ACCESS_PUBLIC, self::ACCESS_PASSWORD, self::ACCESS_WAITING_ROOM, self::ACCESS_INTERNAL, self::ACCESS_INTERNAL_RESTRICTED];
|
||||
public const ACCESS = [self::ACCESS_PUBLIC, self::ACCESS_PASSWORD, self::ACCESS_WAITING_ROOM, self::ACCESS_WAITING_ROOM_ALL, self::ACCESS_INTERNAL, self::ACCESS_INTERNAL_RESTRICTED];
|
||||
|
||||
public $uid;
|
||||
public $name;
|
||||
|
|
83
package.json
83
package.json
|
@ -14,7 +14,7 @@
|
|||
"license": "agpl",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.prod.js",
|
||||
"build": "NODE_ENV=production webpack --progress --config webpack.prod.js",
|
||||
"dev": "NODE_ENV=development webpack --progress --config webpack.dev.js",
|
||||
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js",
|
||||
"test": "run-s --continue-on-error --print-label test:**",
|
||||
|
@ -34,20 +34,20 @@
|
|||
"release:publish": "node scripts/publish-release.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@commitlint/cli": "^9.1.2",
|
||||
"@commitlint/config-conventional": "^9.1.2",
|
||||
"@commitlint/travis-cli": "^9.1.2",
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
"@commitlint/config-conventional": "^16.2.1",
|
||||
"@commitlint/travis-cli": "^16.2.3",
|
||||
"@nextcloud/axios": "^1.3.2",
|
||||
"@nextcloud/router": "^1.0.2",
|
||||
"@nextcloud/router": "^2.0.0",
|
||||
"@octokit/rest": "^18.0.4",
|
||||
"archiver": "^5.0.0",
|
||||
"colors": "^1.4.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"execa": "^4.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"execa": "^6.1.0",
|
||||
"libxmljs": "^0.19.7",
|
||||
"qrcode.react": "^1.0.1",
|
||||
"qrcode.react": "^2.0.0",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"simple-git": "^2.20.1"
|
||||
"simple-git": "^3.3.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
@ -60,58 +60,59 @@
|
|||
"extends @nextcloud/browserslist-config"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@nextcloud/browserslist-config": "^1.0.0",
|
||||
"@nextcloud/browserslist-config": "^2.2.0",
|
||||
"@nextcloud/eslint-plugin": "^2.0.0",
|
||||
"@nextcloud/files": "^1.0.1",
|
||||
"@types/bootstrap": "^4.3.2",
|
||||
"@types/inquirer": "^7.3.1",
|
||||
"@nextcloud/files": "^2.1.0",
|
||||
"@types/bootstrap": "^5.1.9",
|
||||
"@types/inquirer": "^8.2.0",
|
||||
"@types/jquery": "^3.3.35",
|
||||
"@types/node": "^14.6.2",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/webpack": "^4.41.12",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/react": "^17.0.40",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.1",
|
||||
"@typescript-eslint/parser": "^4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"css-loader": "^4.2.2",
|
||||
"dotenv-cli": "^3.1.0",
|
||||
"eslint": "^7.8.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-import-resolver-webpack": "^0.12.1",
|
||||
"eslint-loader": "^4.0.2",
|
||||
"css-loader": "^6.7.1",
|
||||
"dotenv-cli": "^5.0.0",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-import-resolver-webpack": "^0.13.2",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"file-loader": "^6.0.0",
|
||||
"husky": "^4.2.5",
|
||||
"inquirer": "^7.1.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"inquirer": "^8.2.1",
|
||||
"install": "^0.13.0",
|
||||
"node-sass": "^7.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-flip-move": "^3.0.4",
|
||||
"react-hot-loader": "^4.12.20",
|
||||
"react-select": "^3.1.0",
|
||||
"sass-loader": "^10.0.1",
|
||||
"style-loader": "^1.2.0",
|
||||
"stylelint": "^13.7.0",
|
||||
"stylelint-config-recommended-scss": "^4.2.0",
|
||||
"stylelint-scss": "^3.16.0",
|
||||
"ts-loader": "^8.0.3",
|
||||
"react-select": "^5.2.2",
|
||||
"sass-loader": "^12.6.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"stylelint": "^14.5.3",
|
||||
"stylelint-config-recommended-scss": "^5.0.2",
|
||||
"stylelint-scss": "^4.2.0",
|
||||
"ts-loader": "^9.2.8",
|
||||
"typescript": "^4.0.2",
|
||||
"url-loader": "^4.0.0",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-merge": "^5.1.3",
|
||||
"webpack-node-externals": "^2.5.2"
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ export enum Access {
|
|||
Public = 'public',
|
||||
Password = 'password',
|
||||
WaitingRoom = 'waiting_room',
|
||||
WaitingRoomAll = 'waiting_room_all',
|
||||
Internal = 'internal',
|
||||
InternalRestricted = 'internal_restricted',
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export const AccessOptions = {
|
|||
[Access.Public]: t('bbb', 'Public'),
|
||||
[Access.Password]: t('bbb', 'Internal + Password protection for guests'),
|
||||
[Access.WaitingRoom]: t('bbb', 'Internal + Waiting room for guests'),
|
||||
[Access.WaitingRoomAll]: t('bbb', 'Waiting room for all users'),
|
||||
[Access.Internal]: t('bbb', 'Internal'),
|
||||
[Access.InternalRestricted]: t('bbb', 'Internal restricted'),
|
||||
};
|
||||
|
|
|
@ -2,6 +2,9 @@ import axios from '@nextcloud/axios';
|
|||
import { generateOcsUrl, generateUrl } from '@nextcloud/router';
|
||||
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 ExtendedDialogs = typeof OC.dialogs & { message: OC_Dialogs_Message };
|
||||
|
||||
const mimeTypes = [
|
||||
'application/pdf',
|
||||
'application/vnd.oasis.opendocument.presentation',
|
||||
|
@ -18,10 +21,16 @@ const mimeTypes = [
|
|||
'image/png',
|
||||
'text/plain',
|
||||
'text/rtf',
|
||||
];
|
||||
] as const;
|
||||
|
||||
type MimeTypes = typeof mimeTypes[number];
|
||||
|
||||
async function createDirectShare(fileId: number): Promise<string> {
|
||||
const url = generateOcsUrl('apps/dav/api/v1', 1) + 'direct';
|
||||
const url = generateOcsUrl('apps/dav/api/v1/', undefined, {
|
||||
ocsVersion: 1,
|
||||
escape: true,
|
||||
noRewrite: true,
|
||||
}) + 'direct';
|
||||
const createResponse = await axios.post(url, {
|
||||
fileId,
|
||||
});
|
||||
|
@ -29,7 +38,7 @@ async function createDirectShare(fileId: number): Promise<string> {
|
|||
return createResponse.data?.ocs?.data?.url;
|
||||
}
|
||||
|
||||
async function share(fileId: number, filename: string, roomUid) {
|
||||
async function share(fileId: number, filename: string, roomUid: string) {
|
||||
const shareUrl = await createDirectShare(fileId);
|
||||
const joinUrl = generateUrl('/apps/bbb/b/{uid}?u={url}&filename={filename}', {
|
||||
uid: roomUid,
|
||||
|
@ -40,15 +49,59 @@ async function share(fileId: number, filename: string, roomUid) {
|
|||
window.open(joinUrl, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
|
||||
function registerFileAction(fileActions, mime, id, uid, name) {
|
||||
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 title = t('bbb', 'Send file to BBB');
|
||||
|
||||
await (OC.dialogs as ExtendedDialogs).message(initContent, title, 'none', -1, undefined, true, true);
|
||||
|
||||
const rooms = await api.getRooms();
|
||||
|
||||
const container = $('#bbb-file-action').empty();
|
||||
const table = $('<table>').appendTo(container);
|
||||
table.attr('style', 'margin-top: 1em; width: 100%;');
|
||||
|
||||
for (const room of rooms) {
|
||||
const row = $('<tr>');
|
||||
const button = $('<button>');
|
||||
|
||||
button.text(t('bbb', 'Start'));
|
||||
button.addClass('primary');
|
||||
button.attr('type', 'button');
|
||||
button.on('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
share(fileId, filename, room.uid);
|
||||
|
||||
container.parents('.oc-dialog').find('.oc-dialog-close').trigger('click');
|
||||
});
|
||||
|
||||
row.append($('<td>').append(button));
|
||||
row.append($('<td>').attr('style', 'width: 100%;').text(room.name));
|
||||
row.appendTo(table);
|
||||
}
|
||||
|
||||
if (rooms.length > 0) {
|
||||
const description = t('bbb', 'Please select the room in which you like to use the file "{filename}".', { filename });
|
||||
|
||||
container.append(description);
|
||||
container.append(table);
|
||||
} else {
|
||||
container.append($('p').text(t('bbb', 'No rooms available!')));
|
||||
}
|
||||
}
|
||||
|
||||
function registerFileAction(fileActions: any, mime: MimeTypes) {
|
||||
fileActions.registerAction({
|
||||
name: 'bbb-' + id,
|
||||
displayName: name,
|
||||
name: 'bbb',
|
||||
displayName: t('bbb', 'Send to BBB'),
|
||||
mime,
|
||||
permissions: OC.PERMISSION_SHARE,
|
||||
icon: OC.imagePath('bbb', 'app-dark.svg'),
|
||||
actionHandler: (fileName, context) => {
|
||||
share(context.fileInfoModel.id, fileName, uid);
|
||||
console.log('Action handler');
|
||||
|
||||
openDialog(context.fileInfoModel.id, fileName);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -63,11 +116,7 @@ const BBBFileListPlugin = {
|
|||
return;
|
||||
}
|
||||
|
||||
api.getRooms().then(rooms => {
|
||||
rooms.forEach(room => {
|
||||
mimeTypes.forEach(mime => registerFileAction(fileList.fileActions, mime, room.id, room.uid, room.name));
|
||||
});
|
||||
});
|
||||
mimeTypes.forEach(mime => registerFileAction(fileList.fileActions, mime));
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const path = require('path')
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require('path');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
|
@ -19,7 +21,7 @@ module.exports = {
|
|||
],
|
||||
waiting: [
|
||||
path.join(__dirname, 'ts', 'waiting.ts'),
|
||||
]
|
||||
],
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, './js'),
|
||||
|
@ -50,11 +52,6 @@ module.exports = {
|
|||
test: /\.scss$/,
|
||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
use: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
|
@ -71,9 +68,10 @@ module.exports = {
|
|||
],
|
||||
},
|
||||
plugins: [
|
||||
new ESLintPlugin(),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['*', '.tsx', '.ts', '.js', '.scss'],
|
||||
symlinks: false,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: '#cheap-source-map',
|
||||
})
|
||||
mode: 'development',
|
||||
devtool: 'cheap-source-map',
|
||||
});
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const { merge } = require('webpack-merge')
|
||||
const common = require('./webpack.common.js')
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
devtool: '#source-map'
|
||||
})
|
||||
devtool: 'source-map',
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue