commit
334f7bf95a
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ BROWSERIFY = ./node_modules/.bin/browserify
|
||||||
UGLIFYJS = ./node_modules/.bin/uglifyjs
|
UGLIFYJS = ./node_modules/.bin/uglifyjs
|
||||||
EXORCIST = ./node_modules/.bin/exorcist
|
EXORCIST = ./node_modules/.bin/exorcist
|
||||||
CLEANCSS = ./node_modules/.bin/cleancss
|
CLEANCSS = ./node_modules/.bin/cleancss
|
||||||
CSS_FILES = font.css toastr.css main.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css
|
CSS_FILES = font.css toastr.css main.css overlay.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css
|
||||||
DEPLOY_DIR = libs
|
DEPLOY_DIR = libs
|
||||||
BROWSERIFY_FLAGS = -d
|
BROWSERIFY_FLAGS = -d
|
||||||
OUTPUT_DIR = .
|
OUTPUT_DIR = .
|
||||||
|
|
22
app.js
22
app.js
|
@ -23,6 +23,7 @@ import conference from './conference';
|
||||||
import API from './modules/API/API';
|
import API from './modules/API/API';
|
||||||
|
|
||||||
import UIEvents from './service/UI/UIEvents';
|
import UIEvents from './service/UI/UIEvents';
|
||||||
|
import getTokenData from "./modules/TokenData/TokenData";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to push history state with the following parameters:
|
* Tries to push history state with the following parameters:
|
||||||
|
@ -84,10 +85,24 @@ const APP = {
|
||||||
require("./modules/keyboardshortcut/keyboardshortcut");
|
require("./modules/keyboardshortcut/keyboardshortcut");
|
||||||
this.translation = require("./modules/translation/translation");
|
this.translation = require("./modules/translation/translation");
|
||||||
this.configFetch = require("./modules/config/HttpConfigFetch");
|
this.configFetch = require("./modules/config/HttpConfigFetch");
|
||||||
|
this.tokenData = getTokenData();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If JWT token data it will be used for local user settings
|
||||||
|
*/
|
||||||
|
function setTokenData() {
|
||||||
|
let localUser = APP.tokenData.caller;
|
||||||
|
if(localUser) {
|
||||||
|
APP.settings.setEmail((localUser.getEmail() || "").trim());
|
||||||
|
APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
|
||||||
|
APP.settings.setDisplayName((localUser.getName() || "").trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
setTokenData();
|
||||||
var isUIReady = APP.UI.start();
|
var isUIReady = APP.UI.start();
|
||||||
if (isUIReady) {
|
if (isUIReady) {
|
||||||
APP.conference.init({roomName: buildRoomName()}).then(function () {
|
APP.conference.init({roomName: buildRoomName()}).then(function () {
|
||||||
|
@ -100,6 +115,11 @@ function init() {
|
||||||
|
|
||||||
APP.keyboardshortcut.init();
|
APP.keyboardshortcut.init();
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
|
APP.UI.hideRingOverLay();
|
||||||
|
APP.API.sendPostisMessage({
|
||||||
|
method: 'video-conference-left',
|
||||||
|
params: {roomName: APP.conference.roomName}
|
||||||
|
});
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -151,7 +171,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
APP.translation.init(settings.getLanguage());
|
APP.translation.init(settings.getLanguage());
|
||||||
|
|
||||||
APP.API.init();
|
APP.API.init(APP.tokenData.externalAPISettings);
|
||||||
|
|
||||||
obtainConfigAndInit();
|
obtainConfigAndInit();
|
||||||
});
|
});
|
||||||
|
|
135
conference.js
135
conference.js
|
@ -26,6 +26,18 @@ let currentAudioInputDevices, currentVideoInputDevices;
|
||||||
|
|
||||||
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
|
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known custom conference commands.
|
||||||
|
*/
|
||||||
|
const commands = {
|
||||||
|
CONNECTION_QUALITY: "stats",
|
||||||
|
EMAIL: "email",
|
||||||
|
AVATAR_URL: "avatar-url",
|
||||||
|
ETHERPAD: "etherpad",
|
||||||
|
SHARED_VIDEO: "shared-video",
|
||||||
|
CUSTOM_ROLE: "custom-role"
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open Connection. When authentication failed it shows auth dialog.
|
* Open Connection. When authentication failed it shows auth dialog.
|
||||||
* @param roomName the room name to use
|
* @param roomName the room name to use
|
||||||
|
@ -44,17 +56,13 @@ function connect(roomName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Share email with other users.
|
* Share data to other users.
|
||||||
* @param emailCommand the email command
|
* @param command the command
|
||||||
* @param {string} email new email
|
* @param {string} value new value
|
||||||
*/
|
*/
|
||||||
function sendEmail (emailCommand, email) {
|
function sendData (command, value) {
|
||||||
room.sendCommand(emailCommand, {
|
room.removeCommand(command);
|
||||||
value: email,
|
room.sendCommand(command, {value: value});
|
||||||
attributes: {
|
|
||||||
id: room.myUserId()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,7 +152,12 @@ function maybeRedirectToWelcomePage() {
|
||||||
* @returns Promise.
|
* @returns Promise.
|
||||||
*/
|
*/
|
||||||
function disconnectAndShowFeedback(requestFeedback) {
|
function disconnectAndShowFeedback(requestFeedback) {
|
||||||
|
APP.UI.hideRingOverLay();
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
|
APP.API.sendPostisMessage({
|
||||||
|
method: 'video-conference-left',
|
||||||
|
params: {roomName: APP.conference.roomName}
|
||||||
|
});
|
||||||
if (requestFeedback) {
|
if (requestFeedback) {
|
||||||
return APP.UI.requestFeedback();
|
return APP.UI.requestFeedback();
|
||||||
} else {
|
} else {
|
||||||
|
@ -209,6 +222,55 @@ function setCurrentMediaDevices(devices) {
|
||||||
d => d.kind === 'videoinput');
|
d => d.kind === 'videoinput');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the email for the local user
|
||||||
|
* @param email {string} the new email
|
||||||
|
*/
|
||||||
|
function changeLocalEmail(email = '') {
|
||||||
|
email = email.trim();
|
||||||
|
|
||||||
|
if (email === APP.settings.getEmail()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APP.settings.setEmail(email);
|
||||||
|
APP.UI.setUserEmail(room.myUserId(), email);
|
||||||
|
sendData(commands.EMAIL, email);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the local avatar url for the local user
|
||||||
|
* @param avatarUrl {string} the new avatar url
|
||||||
|
*/
|
||||||
|
function changeLocalAvatarUrl(avatarUrl = '') {
|
||||||
|
avatarUrl = avatarUrl.trim();
|
||||||
|
|
||||||
|
if (avatarUrl === APP.settings.getAvatarUrl()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APP.settings.setAvatarUrl(avatarUrl);
|
||||||
|
APP.UI.setUserAvatarUrl(room.myUserId(), avatarUrl);
|
||||||
|
sendData(commands.AVATAR_URL, avatarUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the display name for the local user
|
||||||
|
* @param nickname {string} the new display name
|
||||||
|
*/
|
||||||
|
function changeLocalDisplayName(nickname = '') {
|
||||||
|
nickname = nickname.trim();
|
||||||
|
|
||||||
|
if (nickname === APP.settings.getDisplayName()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APP.settings.setDisplayName(nickname);
|
||||||
|
room.setDisplayName(nickname);
|
||||||
|
APP.UI.changeDisplayName(APP.conference.localId, nickname);
|
||||||
|
}
|
||||||
|
|
||||||
class ConferenceConnector {
|
class ConferenceConnector {
|
||||||
constructor(resolve, reject) {
|
constructor(resolve, reject) {
|
||||||
this._resolve = resolve;
|
this._resolve = resolve;
|
||||||
|
@ -227,6 +289,7 @@ class ConferenceConnector {
|
||||||
}
|
}
|
||||||
_onConferenceFailed(err, ...params) {
|
_onConferenceFailed(err, ...params) {
|
||||||
console.error('CONFERENCE FAILED:', err, ...params);
|
console.error('CONFERENCE FAILED:', err, ...params);
|
||||||
|
APP.UI.hideRingOverLay();
|
||||||
switch (err) {
|
switch (err) {
|
||||||
// room is locked by the password
|
// room is locked by the password
|
||||||
case ConferenceErrors.PASSWORD_REQUIRED:
|
case ConferenceErrors.PASSWORD_REQUIRED:
|
||||||
|
@ -412,6 +475,9 @@ export default {
|
||||||
this._createRoom(tracks);
|
this._createRoom(tracks);
|
||||||
this.isDesktopSharingEnabled =
|
this.isDesktopSharingEnabled =
|
||||||
JitsiMeetJS.isDesktopSharingEnabled();
|
JitsiMeetJS.isDesktopSharingEnabled();
|
||||||
|
if(this.isDesktopSharingEnabled)
|
||||||
|
APP.API.addPostisMessageListener('toggle-share-screen',
|
||||||
|
() => this.toggleScreenSharing());
|
||||||
|
|
||||||
// if user didn't give access to mic or camera or doesn't have
|
// if user didn't give access to mic or camera or doesn't have
|
||||||
// them at all, we disable corresponding toolbar buttons
|
// them at all, we disable corresponding toolbar buttons
|
||||||
|
@ -851,13 +917,7 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Known custom conference commands.
|
* Known custom conference commands.
|
||||||
*/
|
*/
|
||||||
defaults: {
|
defaults: commands,
|
||||||
CONNECTION_QUALITY: "stats",
|
|
||||||
EMAIL: "email",
|
|
||||||
ETHERPAD: "etherpad",
|
|
||||||
SHARED_VIDEO: "shared-video",
|
|
||||||
CUSTOM_ROLE: "custom-role"
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Receives notifications from other participants about commands aka
|
* Receives notifications from other participants about commands aka
|
||||||
* custom events (sent by sendCommand or sendCommandOnce methods).
|
* custom events (sent by sendCommand or sendCommandOnce methods).
|
||||||
|
@ -910,7 +970,11 @@ export default {
|
||||||
this._room = room; // FIXME do not use this
|
this._room = room; // FIXME do not use this
|
||||||
|
|
||||||
let email = APP.settings.getEmail();
|
let email = APP.settings.getEmail();
|
||||||
email && sendEmail(this.commands.defaults.EMAIL, email);
|
email && sendData(this.commands.defaults.EMAIL, email);
|
||||||
|
|
||||||
|
let avatarUrl = APP.settings.getAvatarUrl();
|
||||||
|
avatarUrl && sendData(this.commands.defaults.AVATAR_URL,
|
||||||
|
avatarUrl);
|
||||||
|
|
||||||
let nick = APP.settings.getDisplayName();
|
let nick = APP.settings.getDisplayName();
|
||||||
if (config.useNicks && !nick) {
|
if (config.useNicks && !nick) {
|
||||||
|
@ -1093,6 +1157,10 @@ export default {
|
||||||
// add local streams when joined to the conference
|
// add local streams when joined to the conference
|
||||||
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
||||||
APP.UI.mucJoined();
|
APP.UI.mucJoined();
|
||||||
|
APP.API.sendPostisMessage({
|
||||||
|
method: 'video-conference-joined',
|
||||||
|
params: {roomName: APP.conference.roomName}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
room.on(
|
room.on(
|
||||||
|
@ -1297,33 +1365,20 @@ export default {
|
||||||
APP.UI.initEtherpad(value);
|
APP.UI.initEtherpad(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED, (email = '') => {
|
APP.UI.addListener(UIEvents.EMAIL_CHANGED, changeLocalEmail);
|
||||||
email = email.trim();
|
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
|
||||||
|
APP.UI.setUserEmail(from, data.value);
|
||||||
if (email === APP.settings.getEmail()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
APP.settings.setEmail(email);
|
|
||||||
APP.UI.setUserAvatar(room.myUserId(), email);
|
|
||||||
sendEmail(this.commands.defaults.EMAIL, email);
|
|
||||||
});
|
|
||||||
room.addCommandListener(this.commands.defaults.EMAIL, (data) => {
|
|
||||||
APP.UI.setUserAvatar(data.attributes.id, data.value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, (nickname = '') => {
|
APP.UI.addListener(UIEvents.AVATAR_URL_CHANGED, changeLocalAvatarUrl);
|
||||||
nickname = nickname.trim();
|
|
||||||
|
|
||||||
if (nickname === APP.settings.getDisplayName()) {
|
room.addCommandListener(this.commands.defaults.AVATAR_URL,
|
||||||
return;
|
(data, from) => {
|
||||||
}
|
APP.UI.setUserAvatarUrl(from, data.value);
|
||||||
|
|
||||||
APP.settings.setDisplayName(nickname);
|
|
||||||
room.setDisplayName(nickname);
|
|
||||||
APP.UI.changeDisplayName(APP.conference.localId, nickname);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, changeLocalDisplayName);
|
||||||
|
|
||||||
APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
|
APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
|
||||||
(startAudioMuted, startVideoMuted) => {
|
(startAudioMuted, startVideoMuted) => {
|
||||||
room.setStartMutedPolicy({
|
room.setStartMutedPolicy({
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
* Executes createConnectionExternally function.
|
* Executes createConnectionExternally function.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
var params = getConfigParamsFromUrl();
|
var hashParams = getConfigParamsFromUrl("hash", true);
|
||||||
|
var searchParams = getConfigParamsFromUrl("search", true);
|
||||||
|
|
||||||
//Url params have higher proirity than config params
|
//Url params have higher proirity than config params
|
||||||
var url = config.externalConnectUrl;
|
var url = config.externalConnectUrl;
|
||||||
if(params.hasOwnProperty('config.externalConnectUrl'))
|
if(hashParams.hasOwnProperty('config.externalConnectUrl'))
|
||||||
url = params["config.externalConnectUrl"];
|
url = hashParams["config.externalConnectUrl"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if connect from connection.js was executed and executes the handler
|
* Check if connect from connection.js was executed and executes the handler
|
||||||
|
@ -57,7 +58,8 @@
|
||||||
|
|
||||||
url += "?room=" + room_name;
|
url += "?room=" + room_name;
|
||||||
|
|
||||||
var token = params["config.token"] || config.token;
|
var token = hashParams["config.token"] || config.token ||
|
||||||
|
searchParams.jwt;
|
||||||
if(token)
|
if(token)
|
||||||
url += "&token=" + token;
|
url += "&token=" + token;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,10 @@ html, body{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header_container {
|
||||||
|
z-index: 1014;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar_span {
|
.toolbar_span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1013;
|
||||||
|
background: #000000; /* Old browsers */
|
||||||
|
opacity: 0.75;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay_container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1013;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay_content {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
width: 400px;
|
||||||
|
height: 250px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
position:absolute;
|
||||||
|
margin-top: -125px;
|
||||||
|
margin-left: -200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay_avatar {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 100px;
|
||||||
|
z-index: 1013;
|
||||||
|
float: left;
|
||||||
|
margin-left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay_text {
|
||||||
|
position: relative;
|
||||||
|
width: 400px;
|
||||||
|
z-index: 1013;
|
||||||
|
margin-top: 20px;
|
||||||
|
float: left;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 1010;
|
z-index: 1015;
|
||||||
display: none;
|
display: none;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
@ -121,4 +121,4 @@
|
||||||
border-right-width: 0;
|
border-right-width: 0;
|
||||||
border-left-color: #ffffff;
|
border-left-color: #ffffff;
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ button.toast-close-button {
|
||||||
}
|
}
|
||||||
#toast-container {
|
#toast-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 999999;
|
z-index: 1012;
|
||||||
/*overrides*/
|
/*overrides*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,7 +468,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top:50%;
|
top:50%;
|
||||||
z-index: 10000;
|
z-index: 1011;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -488,7 +488,7 @@
|
||||||
background: rgba(0,0,0,.5);
|
background: rgba(0,0,0,.5);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
color: rgba(255,255,255,.5);
|
color: rgba(255,255,255,.5);
|
||||||
z-index: 10000;
|
z-index: 1011;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centeredVideoLabel {
|
.centeredVideoLabel {
|
||||||
|
@ -506,7 +506,7 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
background: rgba(0,0,0,.5);
|
background: rgba(0,0,0,.5);
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
z-index: 10000;
|
z-index: 1011;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
-webkit-transition: all 2s 2s linear;
|
-webkit-transition: all 2s 2s linear;
|
||||||
transition: all 2s 2s linear;
|
transition: all 2s 2s linear;
|
||||||
|
@ -522,4 +522,4 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,8 @@ case "$1" in
|
||||||
sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
|
sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
|
||||||
|
|
||||||
# Install luajwt
|
# Install luajwt
|
||||||
if ! luarocks install luajwt; then
|
if ! luarocks install jwt; then
|
||||||
echo "Failed to install luajwt - try installing it manually"
|
echo "Failed to install jwt - try installing it manually"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x "/etc/init.d/prosody" ]; then
|
if [ -x "/etc/init.d/prosody" ]; then
|
||||||
|
@ -85,7 +85,7 @@ case "$1" in
|
||||||
else
|
else
|
||||||
echo "Prosody config not found at $PROSODY_HOST_CONFIG - unable to auto-configure token authentication"
|
echo "Prosody config not found at $PROSODY_HOST_CONFIG - unable to auto-configure token authentication"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
abort-upgrade|abort-remove|abort-deconfigure)
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
|
|
@ -241,6 +241,7 @@
|
||||||
<div class="arrow-up"></div>
|
<div class="arrow-up"></div>
|
||||||
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
|
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
|
||||||
<input type="text" id="setEmail" placeholder="E-Mail">
|
<input type="text" id="setEmail" placeholder="E-Mail">
|
||||||
|
<input type="text" id="setAvatarUrl" placeholder="Avatar URL" data-i18n="[placeholder]settings.avatar">
|
||||||
<select id="languages_selectbox"></select>
|
<select id="languages_selectbox"></select>
|
||||||
<div id = "startMutedOptions">
|
<div id = "startMutedOptions">
|
||||||
<label class = "startMutedLabel">
|
<label class = "startMutedLabel">
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"speaker": "Speaker",
|
"speaker": "Speaker",
|
||||||
"defaultNickname": "ex. Jane Pink",
|
"defaultNickname": "ex. Jane Pink",
|
||||||
"defaultLink": "e.g. __url__",
|
"defaultLink": "e.g. __url__",
|
||||||
|
"calling": "Calling __name__ ...",
|
||||||
"welcomepage":{
|
"welcomepage":{
|
||||||
"go": "GO",
|
"go": "GO",
|
||||||
"roomname": "Enter room name",
|
"roomname": "Enter room name",
|
||||||
|
@ -94,7 +95,8 @@
|
||||||
"selectAudioOutput": "Select audio output",
|
"selectAudioOutput": "Select audio output",
|
||||||
"followMe": "Enable follow me",
|
"followMe": "Enable follow me",
|
||||||
"noDevice": "None",
|
"noDevice": "None",
|
||||||
"noPermission": "Permission to use device is not granted"
|
"noPermission": "Permission to use device is not granted",
|
||||||
|
"avatarUrl": "Avatar URL"
|
||||||
},
|
},
|
||||||
"videothumbnail":
|
"videothumbnail":
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
* applications that embed Jitsi Meet
|
* applications that embed Jitsi Meet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import postis from 'postis';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the available commands.
|
* List of the available commands.
|
||||||
* @type {{
|
* @type {{
|
||||||
|
@ -16,7 +18,28 @@
|
||||||
* toggleContactList: toggleContactList
|
* toggleContactList: toggleContactList
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
var commands = {};
|
let commands = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that will execute sendMessage
|
||||||
|
*/
|
||||||
|
let target = window.opener ? window.opener : window.parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of functions that are going to receive the objects passed to this
|
||||||
|
* window
|
||||||
|
*/
|
||||||
|
let messageListeners = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current status (enabled/disabled) of Postis.
|
||||||
|
*/
|
||||||
|
let enablePostis = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current status (enabled/disabled) of Post Message API.
|
||||||
|
*/
|
||||||
|
let enablePostMessage = false;
|
||||||
|
|
||||||
function initCommands() {
|
function initCommands() {
|
||||||
commands = {
|
commands = {
|
||||||
|
@ -91,14 +114,6 @@ function processEvent(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends message to the external application.
|
|
||||||
* @param object
|
|
||||||
*/
|
|
||||||
function sendMessage(object) {
|
|
||||||
window.parent.postMessage(JSON.stringify(object), "*");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a message event from the external application
|
* Processes a message event from the external application
|
||||||
* @param event the message event
|
* @param event the message event
|
||||||
|
@ -107,10 +122,11 @@ function processMessage(event) {
|
||||||
var message;
|
var message;
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(event.data);
|
message = JSON.parse(event.data);
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
console.error("Cannot parse data", event.data);
|
||||||
if(!message.type)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case "command":
|
case "command":
|
||||||
processCommand(message);
|
processCommand(message);
|
||||||
|
@ -119,18 +135,26 @@ function processMessage(event) {
|
||||||
processEvent(message);
|
processEvent(message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unknown type of the message");
|
console.warn("Unknown message type");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends message to the external application.
|
||||||
|
* @param object {object} the object that will be sent as JSON string
|
||||||
|
*/
|
||||||
|
function sendMessage(object) {
|
||||||
|
if(enablePostMessage)
|
||||||
|
target.postMessage(JSON.stringify(object), "*");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the API should be enabled or not.
|
* Check whether the API should be enabled or not.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function isEnabled () {
|
function isEnabled () {
|
||||||
let hash = location.hash;
|
let hash = location.hash;
|
||||||
return hash && hash.indexOf("external=true") > -1 && window.postMessage;
|
return !!(hash && hash.indexOf("external=true") > -1 && window.postMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,7 +173,7 @@ function isEventEnabled (name) {
|
||||||
* @param object data associated with the event
|
* @param object data associated with the event
|
||||||
*/
|
*/
|
||||||
function triggerEvent (name, object) {
|
function triggerEvent (name, object) {
|
||||||
if (isEnabled() && isEventEnabled(name)) {
|
if (isEventEnabled(name) && enablePostMessage) {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: "event",
|
type: "event",
|
||||||
action: "result",
|
action: "result",
|
||||||
|
@ -165,18 +189,34 @@ export default {
|
||||||
* receive information from external applications that embed Jitsi Meet.
|
* receive information from external applications that embed Jitsi Meet.
|
||||||
* It also sends a message to the external application that APIConnector
|
* It also sends a message to the external application that APIConnector
|
||||||
* is initialized.
|
* is initialized.
|
||||||
|
* @param options {object}
|
||||||
|
* @param enablePostis {boolean} if true the postis npm
|
||||||
|
* package for comminication with the parent window will be enabled.
|
||||||
|
* @param enablePostMessage {boolean} if true the postMessageAPI for
|
||||||
|
* comminication with the parent window will be enabled.
|
||||||
*/
|
*/
|
||||||
init: function () {
|
init: function (options = {}) {
|
||||||
if (!isEnabled()) {
|
options.enablePostMessage = options.enablePostMessage || isEnabled();
|
||||||
|
if (!options.enablePostis &&
|
||||||
|
!options.enablePostMessage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
initCommands();
|
enablePostis = options.enablePostis;
|
||||||
if (window.addEventListener) {
|
enablePostMessage = options.enablePostMessage;
|
||||||
window.addEventListener('message', processMessage, false);
|
|
||||||
} else {
|
if(enablePostMessage) {
|
||||||
window.attachEvent('onmessage', processMessage);
|
initCommands();
|
||||||
|
if (window.addEventListener) {
|
||||||
|
window.addEventListener('message', processMessage, false);
|
||||||
|
} else {
|
||||||
|
window.attachEvent('onmessage', processMessage);
|
||||||
|
}
|
||||||
|
sendMessage({type: "system", loaded: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(enablePostis) {
|
||||||
|
this.postis = postis({window: target});
|
||||||
}
|
}
|
||||||
sendMessage({type: "system", loaded: true});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,6 +227,27 @@ export default {
|
||||||
triggerEvent("outgoingMessage", {"message": body});
|
triggerEvent("outgoingMessage", {"message": body});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends message to the external application.
|
||||||
|
* @param options {object}
|
||||||
|
* @param method {string}
|
||||||
|
* @param params {object} the object that will be sent as JSON string
|
||||||
|
*/
|
||||||
|
sendPostisMessage(options) {
|
||||||
|
if(enablePostis)
|
||||||
|
this.postis.send(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds listener for Postis messages.
|
||||||
|
* @param method {string} postis mehtod
|
||||||
|
* @param listener {function}
|
||||||
|
*/
|
||||||
|
addPostisMessageListener (method, listener) {
|
||||||
|
if(enablePostis)
|
||||||
|
this.postis.listen(method, listener);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify external application (if API is enabled) that
|
* Notify external application (if API is enabled) that
|
||||||
* message was received.
|
* message was received.
|
||||||
|
@ -238,14 +299,14 @@ export default {
|
||||||
* Removes the listeners.
|
* Removes the listeners.
|
||||||
*/
|
*/
|
||||||
dispose: function () {
|
dispose: function () {
|
||||||
if (!isEnabled()) {
|
if (enablePostMessage) {
|
||||||
return;
|
if (window.removeEventListener) {
|
||||||
}
|
window.removeEventListener("message", processMessage, false);
|
||||||
|
} else {
|
||||||
if (window.removeEventListener) {
|
window.detachEvent('onmessage', processMessage);
|
||||||
window.removeEventListener("message", processMessage, false);
|
}
|
||||||
} else {
|
|
||||||
window.detachEvent('onmessage', processMessage);
|
|
||||||
}
|
}
|
||||||
|
if(enablePostis)
|
||||||
|
this.postis.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* global getConfigParamsFromUrl, config */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses and handles JWT tokens. Sets config.token.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as jws from "jws";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JWT token from the URL.
|
||||||
|
*/
|
||||||
|
let params = getConfigParamsFromUrl("search", true);
|
||||||
|
let jwt = params.jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a user of conference.
|
||||||
|
*/
|
||||||
|
class User {
|
||||||
|
/**
|
||||||
|
* @param name {string} the name of the user.
|
||||||
|
* @param email {string} the email of the user.
|
||||||
|
* @param avatarUrl {string} the URL for the avatar of the user.
|
||||||
|
*/
|
||||||
|
constructor(name, email, avatarUrl) {
|
||||||
|
this._name = name;
|
||||||
|
this._email = email;
|
||||||
|
this._avatarUrl = avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GETERS START.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name property
|
||||||
|
*/
|
||||||
|
getName() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the email property
|
||||||
|
*/
|
||||||
|
getEmail() {
|
||||||
|
return this._email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of the avatar
|
||||||
|
*/
|
||||||
|
getAvatarUrl() {
|
||||||
|
return this._avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GETERS END.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent the data parsed from the JWT token
|
||||||
|
*/
|
||||||
|
class TokenData{
|
||||||
|
/**
|
||||||
|
* @param {string} the JWT token
|
||||||
|
*/
|
||||||
|
constructor(jwt) {
|
||||||
|
if(!jwt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.jwt = jwt;
|
||||||
|
|
||||||
|
//External API settings
|
||||||
|
this.externalAPISettings = {
|
||||||
|
enablePostis: true
|
||||||
|
};
|
||||||
|
this._decode();
|
||||||
|
// Use JWT param as token if there is not other token set and if the
|
||||||
|
// iss field is not anonymous. If you want to pass data with JWT token
|
||||||
|
// but you don't want to pass the JWT token for verification the iss
|
||||||
|
// field should be set to "anonymous"
|
||||||
|
if(!config.token && this.payload && this.payload.iss !== "anonymous")
|
||||||
|
config.token = jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the JWT token and sets the decoded data to properties.
|
||||||
|
*/
|
||||||
|
_decode() {
|
||||||
|
this.decodedJWT = jws.decode(jwt);
|
||||||
|
if(!this.decodedJWT || !this.decodedJWT.payload)
|
||||||
|
return;
|
||||||
|
this.payload = this.decodedJWT.payload;
|
||||||
|
if(!this.payload.context)
|
||||||
|
return;
|
||||||
|
let callerData = this.payload.context.user;
|
||||||
|
let calleeData = this.payload.context.callee;
|
||||||
|
if(callerData)
|
||||||
|
this.caller = new User(callerData.name, callerData.email,
|
||||||
|
callerData.avatarUrl);
|
||||||
|
if(calleeData)
|
||||||
|
this.callee = new User(calleeData.name, calleeData.email,
|
||||||
|
calleeData.avatarUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the TokenData instance.
|
||||||
|
*/
|
||||||
|
let data = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data variable. Creates new TokenData instance if <tt>data</tt>
|
||||||
|
* variable is null.
|
||||||
|
*/
|
||||||
|
export default function getTokenData() {
|
||||||
|
if(!data)
|
||||||
|
data = new TokenData(jwt);
|
||||||
|
return data;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import FilmStrip from "./videolayout/FilmStrip";
|
||||||
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
||||||
import Settings from "./../settings/Settings";
|
import Settings from "./../settings/Settings";
|
||||||
import { reload } from '../util/helpers';
|
import { reload } from '../util/helpers';
|
||||||
|
import RingOverlay from "./ring_overlay/RingOverlay";
|
||||||
|
|
||||||
var EventEmitter = require("events");
|
var EventEmitter = require("events");
|
||||||
UI.messageHandler = require("./util/MessageHandler");
|
UI.messageHandler = require("./util/MessageHandler");
|
||||||
|
@ -277,7 +278,7 @@ UI.initConference = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we configure our avatar id, before creating avatar for us
|
// Make sure we configure our avatar id, before creating avatar for us
|
||||||
UI.setUserAvatar(id, Settings.getEmail());
|
UI.setUserEmail(id, Settings.getEmail());
|
||||||
|
|
||||||
Toolbar.checkAutoEnableDesktopSharing();
|
Toolbar.checkAutoEnableDesktopSharing();
|
||||||
|
|
||||||
|
@ -388,6 +389,7 @@ UI.start = function () {
|
||||||
|
|
||||||
registerListeners();
|
registerListeners();
|
||||||
|
|
||||||
|
ToolbarToggler.init();
|
||||||
BottomToolbar.init();
|
BottomToolbar.init();
|
||||||
FilmStrip.init(eventEmitter);
|
FilmStrip.init(eventEmitter);
|
||||||
|
|
||||||
|
@ -470,6 +472,10 @@ UI.start = function () {
|
||||||
SettingsMenu.init(eventEmitter);
|
SettingsMenu.init(eventEmitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(APP.tokenData.callee) {
|
||||||
|
UI.showRingOverLay();
|
||||||
|
}
|
||||||
|
|
||||||
// Return true to indicate that the UI has been fully started and
|
// Return true to indicate that the UI has been fully started and
|
||||||
// conference ready.
|
// conference ready.
|
||||||
return true;
|
return true;
|
||||||
|
@ -551,6 +557,7 @@ UI.getSharedDocumentManager = function () {
|
||||||
* @param {string} displayName user nickname
|
* @param {string} displayName user nickname
|
||||||
*/
|
*/
|
||||||
UI.addUser = function (id, displayName) {
|
UI.addUser = function (id, displayName) {
|
||||||
|
UI.hideRingOverLay();
|
||||||
ContactList.addContact(id);
|
ContactList.addContact(id);
|
||||||
|
|
||||||
messageHandler.notify(
|
messageHandler.notify(
|
||||||
|
@ -565,7 +572,7 @@ UI.addUser = function (id, displayName) {
|
||||||
VideoLayout.addParticipantContainer(id);
|
VideoLayout.addParticipantContainer(id);
|
||||||
|
|
||||||
// Configure avatar
|
// Configure avatar
|
||||||
UI.setUserAvatar(id);
|
UI.setUserEmail(id);
|
||||||
|
|
||||||
// set initial display name
|
// set initial display name
|
||||||
if(displayName)
|
if(displayName)
|
||||||
|
@ -800,21 +807,41 @@ UI.dockToolbar = function (isDock) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update user avatar.
|
* Updates the avatar for participant.
|
||||||
* @param {string} id user id
|
* @param {string} id user id
|
||||||
* @param {stirng} email user email
|
* @param {stirng} avatarUrl the URL for the avatar
|
||||||
*/
|
*/
|
||||||
UI.setUserAvatar = function (id, email) {
|
function changeAvatar(id, avatarUrl) {
|
||||||
// update avatar
|
|
||||||
Avatar.setUserAvatar(id, email);
|
|
||||||
|
|
||||||
var avatarUrl = Avatar.getAvatarUrl(id);
|
|
||||||
|
|
||||||
VideoLayout.changeUserAvatar(id, avatarUrl);
|
VideoLayout.changeUserAvatar(id, avatarUrl);
|
||||||
ContactList.changeUserAvatar(id, avatarUrl);
|
ContactList.changeUserAvatar(id, avatarUrl);
|
||||||
if (APP.conference.isLocalId(id)) {
|
if (APP.conference.isLocalId(id)) {
|
||||||
SettingsMenu.changeAvatar(avatarUrl);
|
SettingsMenu.changeAvatar(avatarUrl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user email.
|
||||||
|
* @param {string} id user id
|
||||||
|
* @param {stirng} email user email
|
||||||
|
*/
|
||||||
|
UI.setUserEmail = function (id, email) {
|
||||||
|
// update avatar
|
||||||
|
Avatar.setUserEmail(id, email);
|
||||||
|
|
||||||
|
changeAvatar(id, Avatar.getAvatarUrl(id));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user avatar URL.
|
||||||
|
* @param {string} id user id
|
||||||
|
* @param {stirng} url user avatar url
|
||||||
|
*/
|
||||||
|
UI.setUserAvatarUrl = function (id, url) {
|
||||||
|
// update avatar
|
||||||
|
Avatar.setUserAvatarUrl(id, url);
|
||||||
|
|
||||||
|
changeAvatar(id, Avatar.getAvatarUrl(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1216,7 +1243,7 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
|
||||||
: ``;
|
: ``;
|
||||||
let doNotShowWarningAgainSection = showDoNotShowWarning
|
let doNotShowWarningAgainSection = showDoNotShowWarning
|
||||||
? `<label>
|
? `<label>
|
||||||
<input type='checkbox' id='doNotShowWarningAgain'>
|
<input type='checkbox' id='doNotShowWarningAgain'>
|
||||||
<span data-i18n='dialog.doNotShowWarningAgain'></span>
|
<span data-i18n='dialog.doNotShowWarningAgain'></span>
|
||||||
</label>`
|
</label>`
|
||||||
: ``;
|
: ``;
|
||||||
|
@ -1343,4 +1370,19 @@ UI.enableMicrophoneButton = function () {
|
||||||
Toolbar.markAudioIconAsDisabled(false);
|
Toolbar.markAudioIconAsDisabled(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let bottomToolbarEnabled = null;
|
||||||
|
|
||||||
|
UI.showRingOverLay = function () {
|
||||||
|
RingOverlay.show(APP.tokenData.callee);
|
||||||
|
ToolbarToggler.setAlwaysVisibleToolbar(true);
|
||||||
|
FilmStrip.toggleFilmStrip(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
UI.hideRingOverLay = function () {
|
||||||
|
if(!RingOverlay.hide())
|
||||||
|
return;
|
||||||
|
ToolbarToggler.resetAlwaysVisibleToolbar();
|
||||||
|
FilmStrip.toggleFilmStrip(true);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = UI;
|
module.exports = UI;
|
||||||
|
|
|
@ -3,6 +3,19 @@
|
||||||
let users = {};
|
let users = {};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* Sets prop in users object.
|
||||||
|
* @param id {string} user id
|
||||||
|
* @param prop {string} name of the prop
|
||||||
|
* @param val {string} value to be set
|
||||||
|
*/
|
||||||
|
_setUserProp: function (id, prop, val) {
|
||||||
|
if(!val || (users[id] && users[id][prop] === val))
|
||||||
|
return;
|
||||||
|
if(!users[id])
|
||||||
|
users[id] = {};
|
||||||
|
users[id][prop] = val;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the user's avatar in the settings menu(if local user), contact list
|
* Sets the user's avatar in the settings menu(if local user), contact list
|
||||||
|
@ -10,13 +23,18 @@ export default {
|
||||||
* @param id id of the user
|
* @param id id of the user
|
||||||
* @param email email or nickname to be used as a hash
|
* @param email email or nickname to be used as a hash
|
||||||
*/
|
*/
|
||||||
setUserAvatar: function (id, email) {
|
setUserEmail: function (id, email) {
|
||||||
if (email) {
|
this._setUserProp(id, "email", email);
|
||||||
if (users[id] === email) {
|
},
|
||||||
return;
|
|
||||||
}
|
/**
|
||||||
users[id] = email;
|
* Sets the user's avatar in the settings menu(if local user), contact list
|
||||||
}
|
* and thumbnail
|
||||||
|
* @param id id of the user
|
||||||
|
* @param url the url for the avatar
|
||||||
|
*/
|
||||||
|
setUserAvatarUrl: function (id, url) {
|
||||||
|
this._setUserProp(id, "url", url);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +52,15 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarId = users[userId];
|
let avatarId = null;
|
||||||
|
const user = users[userId];
|
||||||
|
|
||||||
|
if(user) {
|
||||||
|
if(user.url)
|
||||||
|
return users[userId].url;
|
||||||
|
|
||||||
|
avatarId = users[userId].email;
|
||||||
|
}
|
||||||
|
|
||||||
// If the ID looks like an email, we'll use gravatar.
|
// If the ID looks like an email, we'll use gravatar.
|
||||||
// Otherwise, it's a random avatar, and we'll use the configured
|
// Otherwise, it's a random avatar, and we'll use the configured
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows ring overlay
|
||||||
|
*/
|
||||||
|
class RingOverlay {
|
||||||
|
/**
|
||||||
|
* @param callee instance of User class from TokenData.js
|
||||||
|
*/
|
||||||
|
constructor(callee) {
|
||||||
|
this.callee = callee;
|
||||||
|
this._buildHtml();
|
||||||
|
this.audio = $("#ring_overlay_ringing");
|
||||||
|
this.audio[0].play();
|
||||||
|
this._setAudioTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and appends the ring overlay to the html document
|
||||||
|
*/
|
||||||
|
_buildHtml() {
|
||||||
|
$("body").append("<div class='overlay_container' >" +
|
||||||
|
"<div class='overlay' /><div class='overlay_content'>" +
|
||||||
|
"<img class='overlay_avatar' src='" +
|
||||||
|
this.callee.getAvatarUrl() + "' />" +
|
||||||
|
"<span data-i18n='calling' data-i18n-options='" +
|
||||||
|
JSON.stringify({name: this.callee.getName()}) +
|
||||||
|
"' class='overlay_text'>Calling " +
|
||||||
|
this.callee.getName() + "...</span></div>" +
|
||||||
|
"<audio id='ring_overlay_ringing' src='/sounds/ring.ogg' /></div>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the interval that is going to play the ringing sound.
|
||||||
|
*/
|
||||||
|
_setAudioTimeout() {
|
||||||
|
this.interval = setInterval( () => {
|
||||||
|
this.audio[0].play();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys and clears all the objects (html elements and audio interval)
|
||||||
|
* related to the ring overlay.
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
if(this.interval)
|
||||||
|
clearInterval(this.interval);
|
||||||
|
$(".overlay_container").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the current ring overlay instance.
|
||||||
|
* Note: We want to have only 1 instance at a time.
|
||||||
|
*/
|
||||||
|
let overlay = null;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* Shows the ring overlay for the passed callee.
|
||||||
|
* @param callee {class User} the callee. Instance of User class from
|
||||||
|
* TokenData.js
|
||||||
|
*/
|
||||||
|
show(callee) {
|
||||||
|
if(overlay) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
overlay = new RingOverlay(callee);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Hides the ring overlay. Destroys all the elements related to the ring
|
||||||
|
* overlay.
|
||||||
|
*/
|
||||||
|
hide() {
|
||||||
|
if(!overlay)
|
||||||
|
return false;
|
||||||
|
overlay.destroy();
|
||||||
|
overlay = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
|
@ -81,6 +81,12 @@ export default {
|
||||||
function updateEmail () {
|
function updateEmail () {
|
||||||
emitter.emit(UIEvents.EMAIL_CHANGED, $('#setEmail').val());
|
emitter.emit(UIEvents.EMAIL_CHANGED, $('#setEmail').val());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AVATAR URL CHANGED
|
||||||
|
function updateAvatarUrl () {
|
||||||
|
emitter.emit(UIEvents.AVATAR_URL_CHANGED, $('#setAvatarUrl').val());
|
||||||
|
}
|
||||||
|
|
||||||
$('#setEmail')
|
$('#setEmail')
|
||||||
.val(Settings.getEmail())
|
.val(Settings.getEmail())
|
||||||
.keyup(function (event) {
|
.keyup(function (event) {
|
||||||
|
@ -89,6 +95,14 @@ export default {
|
||||||
}
|
}
|
||||||
}).focusout(updateEmail);
|
}).focusout(updateEmail);
|
||||||
|
|
||||||
|
$('#setAvatarUrl')
|
||||||
|
.val(Settings.getAvatarUrl())
|
||||||
|
.keyup(function (event) {
|
||||||
|
if (event.keyCode === 13) { // enter
|
||||||
|
updateAvatarUrl();
|
||||||
|
}
|
||||||
|
}).focusout(updateAvatarUrl);
|
||||||
|
|
||||||
|
|
||||||
// START MUTED
|
// START MUTED
|
||||||
$("#startMutedOptions").change(function () {
|
$("#startMutedOptions").change(function () {
|
||||||
|
|
|
@ -7,6 +7,10 @@ import FilmStrip from '../videolayout/FilmStrip.js';
|
||||||
|
|
||||||
let toolbarTimeoutObject;
|
let toolbarTimeoutObject;
|
||||||
let toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
let toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||||
|
/**
|
||||||
|
* If true the toolbar will be always displayed
|
||||||
|
*/
|
||||||
|
let alwaysVisibleToolbar = false;
|
||||||
|
|
||||||
function showDesktopSharingButton() {
|
function showDesktopSharingButton() {
|
||||||
if (APP.conference.isDesktopSharingEnabled &&
|
if (APP.conference.isDesktopSharingEnabled &&
|
||||||
|
@ -21,7 +25,7 @@ function showDesktopSharingButton() {
|
||||||
* Hides the toolbar.
|
* Hides the toolbar.
|
||||||
*/
|
*/
|
||||||
function hideToolbar() {
|
function hideToolbar() {
|
||||||
if (config.alwaysVisibleToolbar) {
|
if (alwaysVisibleToolbar) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +44,25 @@ function hideToolbar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToolbarToggler = {
|
const ToolbarToggler = {
|
||||||
|
/**
|
||||||
|
* Initializes the ToolbarToggler
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets the value of alwaysVisibleToolbar variable.
|
||||||
|
* @param value {boolean} the new value of alwaysVisibleToolbar variable
|
||||||
|
*/
|
||||||
|
setAlwaysVisibleToolbar(value) {
|
||||||
|
alwaysVisibleToolbar = value;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Resets the value of alwaysVisibleToolbar variable to the default one.
|
||||||
|
*/
|
||||||
|
resetAlwaysVisibleToolbar() {
|
||||||
|
alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Shows the main toolbar.
|
* Shows the main toolbar.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@ let cameraDeviceId = '';
|
||||||
let micDeviceId = '';
|
let micDeviceId = '';
|
||||||
let welcomePageDisabled = false;
|
let welcomePageDisabled = false;
|
||||||
let localFlipX = null;
|
let localFlipX = null;
|
||||||
|
let avatarUrl = '';
|
||||||
|
|
||||||
function supportsLocalStorage() {
|
function supportsLocalStorage() {
|
||||||
try {
|
try {
|
||||||
|
@ -34,6 +35,7 @@ if (supportsLocalStorage()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
email = UIUtil.unescapeHtml(window.localStorage.email || '');
|
email = UIUtil.unescapeHtml(window.localStorage.email || '');
|
||||||
|
avatarUrl = UIUtil.unescapeHtml(window.localStorage.avatarUrl || '');
|
||||||
localFlipX = JSON.parse(window.localStorage.localFlipX || true);
|
localFlipX = JSON.parse(window.localStorage.localFlipX || true);
|
||||||
displayName = UIUtil.unescapeHtml(window.localStorage.displayname || '');
|
displayName = UIUtil.unescapeHtml(window.localStorage.displayname || '');
|
||||||
language = window.localStorage.language;
|
language = window.localStorage.language;
|
||||||
|
@ -98,6 +100,24 @@ export default {
|
||||||
return email;
|
return email;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets new avatarUrl for local user and saves it to the local storage.
|
||||||
|
* @param {string} newAvatarUrl new avatarUrl for the local user
|
||||||
|
*/
|
||||||
|
setAvatarUrl: function (newAvatarUrl) {
|
||||||
|
avatarUrl = newAvatarUrl;
|
||||||
|
window.localStorage.avatarUrl = UIUtil.escapeHtml(newAvatarUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns avatarUrl address of the local user.
|
||||||
|
* @returns {string} avatarUrl
|
||||||
|
*/
|
||||||
|
getAvatarUrl: function () {
|
||||||
|
return avatarUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
getLanguage () {
|
getLanguage () {
|
||||||
return language;
|
return language;
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
"retry": "0.6.1",
|
"retry": "0.6.1",
|
||||||
"strophe": "^1.2.2",
|
"strophe": "^1.2.2",
|
||||||
"strophejs-plugins": "^0.0.6",
|
"strophejs-plugins": "^0.0.6",
|
||||||
"toastr": "^2.0.3"
|
"toastr": "^2.0.3",
|
||||||
|
"postis": "^2.2.0",
|
||||||
|
"jws": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "11.1.x",
|
"browserify": "11.1.x",
|
||||||
|
|
|
@ -15,6 +15,7 @@ local host = module.host;
|
||||||
local appId = module:get_option_string("app_id");
|
local appId = module:get_option_string("app_id");
|
||||||
local appSecret = module:get_option_string("app_secret");
|
local appSecret = module:get_option_string("app_secret");
|
||||||
local allowEmptyToken = module:get_option_boolean("allow_empty_token");
|
local allowEmptyToken = module:get_option_boolean("allow_empty_token");
|
||||||
|
local disableRoomNameConstraints = module:get_option_boolean("disable_room_name_constraints");
|
||||||
|
|
||||||
if allowEmptyToken == true then
|
if allowEmptyToken == true then
|
||||||
module:log("warn", "WARNING - empty tokens allowed");
|
module:log("warn", "WARNING - empty tokens allowed");
|
||||||
|
@ -79,7 +80,7 @@ function provider.get_sasl_handler(session)
|
||||||
|
|
||||||
-- here we check if 'room' claim exists
|
-- here we check if 'room' claim exists
|
||||||
local room, roomErr = token_util.get_room_name(token, appSecret);
|
local room, roomErr = token_util.get_room_name(token, appSecret);
|
||||||
if room == nil then
|
if room == nil and disableRoomNameConstraints ~= true then
|
||||||
if roomErr == nil then
|
if roomErr == nil then
|
||||||
roomErr = "'room' claim is missing";
|
roomErr = "'room' claim is missing";
|
||||||
end
|
end
|
||||||
|
@ -88,7 +89,7 @@ function provider.get_sasl_handler(session)
|
||||||
|
|
||||||
-- now verify the whole token
|
-- now verify the whole token
|
||||||
local result, msg
|
local result, msg
|
||||||
= token_util.verify_token(token, appId, appSecret, room);
|
= token_util.verify_token(token, appId, appSecret, room, disableRoomNameConstraints);
|
||||||
if result == true then
|
if result == true then
|
||||||
-- Binds room name to the session which is later checked on MUC join
|
-- Binds room name to the session which is later checked on MUC join
|
||||||
session.jitsi_meet_room = room;
|
session.jitsi_meet_room = room;
|
||||||
|
@ -121,4 +122,3 @@ local function anonymous(self, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
sasl.registerMechanism("ANONYMOUS", {"anonymous"}, anonymous);
|
sasl.registerMechanism("ANONYMOUS", {"anonymous"}, anonymous);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ end
|
||||||
local appId = parentCtx:get_option_string("app_id");
|
local appId = parentCtx:get_option_string("app_id");
|
||||||
local appSecret = parentCtx:get_option_string("app_secret");
|
local appSecret = parentCtx:get_option_string("app_secret");
|
||||||
local allowEmptyToken = parentCtx:get_option_boolean("allow_empty_token");
|
local allowEmptyToken = parentCtx:get_option_boolean("allow_empty_token");
|
||||||
|
local disableRoomNameConstraints = parentCtx:get_option_boolean("disable_room_name_constraints")
|
||||||
|
|
||||||
log("debug",
|
log("debug",
|
||||||
"%s - starting MUC token verifier app_id: %s app_secret: %s allow empty: %s",
|
"%s - starting MUC token verifier app_id: %s app_secret: %s allow empty: %s",
|
||||||
|
@ -35,13 +36,6 @@ local function verify_user(session, stanza)
|
||||||
tostring(session.auth_token),
|
tostring(session.auth_token),
|
||||||
tostring(session.jitsi_meet_room));
|
tostring(session.jitsi_meet_room));
|
||||||
|
|
||||||
if allowEmptyToken and session.auth_token == nil then
|
|
||||||
module:log(
|
|
||||||
"debug",
|
|
||||||
"Skipped room token verification - empty tokens are allowed");
|
|
||||||
return nil;
|
|
||||||
end
|
|
||||||
|
|
||||||
-- token not required for admin users
|
-- token not required for admin users
|
||||||
local user_jid = stanza.attr.from;
|
local user_jid = stanza.attr.from;
|
||||||
if is_admin(user_jid) then
|
if is_admin(user_jid) then
|
||||||
|
@ -49,6 +43,13 @@ local function verify_user(session, stanza)
|
||||||
return nil;
|
return nil;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if allowEmptyToken and session.auth_token == nil then
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"Skipped room token verification - empty tokens are allowed");
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
local room = string.match(stanza.attr.to, "^(%w+)@");
|
local room = string.match(stanza.attr.to, "^(%w+)@");
|
||||||
log("debug", "Will verify token for user: %s, room: %s ", user_jid, room);
|
log("debug", "Will verify token for user: %s, room: %s ", user_jid, room);
|
||||||
if room == nil then
|
if room == nil then
|
||||||
|
@ -59,7 +60,7 @@ local function verify_user(session, stanza)
|
||||||
|
|
||||||
local token = session.auth_token;
|
local token = session.auth_token;
|
||||||
local auth_room = session.jitsi_meet_room;
|
local auth_room = session.jitsi_meet_room;
|
||||||
if room ~= auth_room then
|
if room ~= auth_room and disableRoomNameConstraints ~= true then
|
||||||
log("error", "Token %s not allowed to join: %s",
|
log("error", "Token %s not allowed to join: %s",
|
||||||
tostring(token), tostring(auth_room));
|
tostring(token), tostring(auth_room));
|
||||||
session.send(
|
session.send(
|
||||||
|
@ -81,4 +82,3 @@ module:hook("muc-occupant-pre-join", function(event)
|
||||||
log("debug", "pre join: %s %s", tostring(room), tostring(stanza));
|
log("debug", "pre join: %s %s", tostring(room), tostring(stanza));
|
||||||
return verify_user(origin, stanza);
|
return verify_user(origin, stanza);
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
-- Token authentication
|
-- Token authentication
|
||||||
-- Copyright (C) 2015 Atlassian
|
-- Copyright (C) 2015 Atlassian
|
||||||
|
|
||||||
local jwt = require "luajwt";
|
local jwt = require "jwt";
|
||||||
|
|
||||||
local _M = {};
|
local _M = {};
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ local function _get_room_name(token, appSecret)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _verify_token(token, appId, appSecret, roomName)
|
local function _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
|
||||||
|
|
||||||
local claims, err = jwt.decode(token, appSecret, true);
|
local claims, err = jwt.decode(token, appSecret, true);
|
||||||
if claims == nil then
|
if claims == nil then
|
||||||
|
@ -30,22 +30,22 @@ local function _verify_token(token, appId, appSecret, roomName)
|
||||||
end
|
end
|
||||||
|
|
||||||
local roomClaim = claims["room"];
|
local roomClaim = claims["room"];
|
||||||
if roomClaim == nil then
|
if roomClaim == nil and disableRoomNameConstraints ~= true then
|
||||||
return nil, "'room' claim is missing";
|
return nil, "'room' claim is missing";
|
||||||
end
|
end
|
||||||
if roomName ~= nil and roomName ~= roomClaim then
|
if roomName ~= nil and roomName ~= roomClaim and disableRoomNameConstraints ~= true then
|
||||||
return nil, "Invalid room name('room' claim)";
|
return nil, "Invalid room name('room' claim)";
|
||||||
end
|
end
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.verify_token(token, appId, appSecret, roomName)
|
function _M.verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
|
||||||
return _verify_token(token, appId, appSecret, roomName);
|
return _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints);
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.get_room_name(token, appSecret)
|
function _M.get_room_name(token, appSecret)
|
||||||
return _get_room_name(token, appSecret);
|
return _get_room_name(token, appSecret);
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M;
|
return _M;
|
||||||
|
|
|
@ -14,6 +14,10 @@ export default {
|
||||||
* Notifies that local user changed email.
|
* Notifies that local user changed email.
|
||||||
*/
|
*/
|
||||||
EMAIL_CHANGED: "UI.email_changed",
|
EMAIL_CHANGED: "UI.email_changed",
|
||||||
|
/**
|
||||||
|
* Notifies that local user changed avatar url.
|
||||||
|
*/
|
||||||
|
AVATAR_URL_CHANGED: "UI.avatar_url_changed",
|
||||||
/**
|
/**
|
||||||
* Notifies that "start muted" settings changed.
|
* Notifies that "start muted" settings changed.
|
||||||
*/
|
*/
|
||||||
|
|
Binary file not shown.
Binary file not shown.
29
utils.js
29
utils.js
|
@ -36,17 +36,32 @@ function getRoomName () {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the hash parameters from the URL and returns them as a JS object.
|
* Parses the parameters from the URL and returns them as a JS object.
|
||||||
|
* @param source {string} values - "hash"/"search" if "search" the parameters
|
||||||
|
* will parsed from location.search otherwise from location.hash
|
||||||
|
* @param dontParse if false or undefined some transformations
|
||||||
|
* (for parsing the value as JSON) are going to be executed
|
||||||
*/
|
*/
|
||||||
function getConfigParamsFromUrl() {
|
function getConfigParamsFromUrl(source, dontParse) {
|
||||||
if (!location.hash)
|
var paramStr = (source === "search")? location.search : location.hash;
|
||||||
|
if (!paramStr)
|
||||||
return {};
|
return {};
|
||||||
var hash = location.hash.substr(1);
|
paramStr = paramStr.substr(1);
|
||||||
var result = {};
|
var result = {};
|
||||||
hash.split("&").forEach(function (part) {
|
paramStr.split("&").forEach(function (part) {
|
||||||
var item = part.split("=");
|
var item = part.split("=");
|
||||||
result[item[0]] = JSON.parse(
|
var value;
|
||||||
decodeURIComponent(item[1]).replace(/\\&/, "&"));
|
try {
|
||||||
|
value = (dontParse)? item[1] : JSON.parse(
|
||||||
|
decodeURIComponent(item[1]).replace(/\\&/, "&"));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to parse URL argument", e);
|
||||||
|
if(window.onerror)
|
||||||
|
window.onerror("Failed to parse URL argument", null, null,
|
||||||
|
null, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result[item[0]] = value;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue