Bläddra i källkod

JWT client support

j8
hristoterezov 9 år sedan
förälder
incheckning
8deb003ef6

+ 1
- 1
Makefile Visa fil

@@ -3,7 +3,7 @@ BROWSERIFY = ./node_modules/.bin/browserify
3 3
 UGLIFYJS = ./node_modules/.bin/uglifyjs
4 4
 EXORCIST = ./node_modules/.bin/exorcist
5 5
 CLEANCSS = ./node_modules/.bin/cleancss
6
-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
6
+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
7 7
 DEPLOY_DIR = libs
8 8
 BROWSERIFY_FLAGS = -d
9 9
 OUTPUT_DIR = .

+ 21
- 1
app.js Visa fil

@@ -23,6 +23,7 @@ import conference from './conference';
23 23
 import API from './modules/API/API';
24 24
 
25 25
 import UIEvents from './service/UI/UIEvents';
26
+import getTokenData from "./modules/TokenData/TokenData";
26 27
 
27 28
 /**
28 29
  * Tries to push history state with the following parameters:
@@ -84,10 +85,24 @@ const APP = {
84 85
             require("./modules/keyboardshortcut/keyboardshortcut");
85 86
         this.translation = require("./modules/translation/translation");
86 87
         this.configFetch = require("./modules/config/HttpConfigFetch");
88
+        this.tokenData = getTokenData();
87 89
     }
88 90
 };
89 91
 
92
+/**
93
+ * If JWT token data it will be used for local user settings
94
+ */
95
+function setTokenData() {
96
+    let localUser = APP.tokenData.caller;
97
+    if(localUser) {
98
+        APP.settings.setEmail((localUser.getEmail() || "").trim());
99
+        APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
100
+        APP.settings.setDisplayName((localUser.getName() || "").trim());
101
+    }
102
+}
103
+
90 104
 function init() {
105
+    setTokenData();
91 106
     var isUIReady = APP.UI.start();
92 107
     if (isUIReady) {
93 108
         APP.conference.init({roomName: buildRoomName()}).then(function () {
@@ -100,6 +115,11 @@ function init() {
100 115
 
101 116
             APP.keyboardshortcut.init();
102 117
         }).catch(function (err) {
118
+            APP.UI.hideRingOverLay();
119
+            APP.API.sendPostisMessage({
120
+                method: 'video-conference-left',
121
+                params: {roomName: APP.conference.roomName}
122
+            });
103 123
             console.error(err);
104 124
         });
105 125
     }
@@ -151,7 +171,7 @@ $(document).ready(function () {
151 171
 
152 172
     APP.translation.init(settings.getLanguage());
153 173
 
154
-    APP.API.init();
174
+    APP.API.init(APP.tokenData.externalAPISettings);
155 175
 
156 176
     obtainConfigAndInit();
157 177
 });

+ 95
- 40
conference.js Visa fil

@@ -26,6 +26,18 @@ let currentAudioInputDevices, currentVideoInputDevices;
26 26
 
27 27
 import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
28 28
 
29
+/**
30
+ * Known custom conference commands.
31
+ */
32
+const commands = {
33
+    CONNECTION_QUALITY: "stats",
34
+    EMAIL: "email",
35
+    AVATAR_URL: "avatar-url",
36
+    ETHERPAD: "etherpad",
37
+    SHARED_VIDEO: "shared-video",
38
+    CUSTOM_ROLE: "custom-role"
39
+};
40
+
29 41
 /**
30 42
  * Open Connection. When authentication failed it shows auth dialog.
31 43
  * @param roomName the room name to use
@@ -44,17 +56,13 @@ function connect(roomName) {
44 56
 }
45 57
 
46 58
 /**
47
- * Share email with other users.
48
- * @param emailCommand the email command
49
- * @param {string} email new email
59
+ * Share data to other users.
60
+ * @param command the command
61
+ * @param {string} value new value
50 62
  */
51
-function sendEmail (emailCommand, email) {
52
-    room.sendCommand(emailCommand, {
53
-        value: email,
54
-        attributes: {
55
-            id: room.myUserId()
56
-        }
57
-    });
63
+function sendData (command, value) {
64
+    room.removeCommand(command);
65
+    room.sendCommand(command, {value: value});
58 66
 }
59 67
 
60 68
 /**
@@ -144,7 +152,12 @@ function maybeRedirectToWelcomePage() {
144 152
  * @returns Promise.
145 153
  */
146 154
 function disconnectAndShowFeedback(requestFeedback) {
155
+    APP.UI.hideRingOverLay();
147 156
     connection.disconnect();
157
+    APP.API.sendPostisMessage({
158
+        method: 'video-conference-left',
159
+        params: {roomName: APP.conference.roomName}
160
+    });
148 161
     if (requestFeedback) {
149 162
         return APP.UI.requestFeedback();
150 163
     } else {
@@ -209,6 +222,55 @@ function setCurrentMediaDevices(devices) {
209 222
         d => d.kind === 'videoinput');
210 223
 }
211 224
 
225
+/**
226
+ * Changes the email for the local user
227
+ * @param email {string} the new email
228
+ */
229
+function changeLocalEmail(email = '') {
230
+    email = email.trim();
231
+
232
+    if (email === APP.settings.getEmail()) {
233
+        return;
234
+    }
235
+
236
+    APP.settings.setEmail(email);
237
+    APP.UI.setUserEmail(room.myUserId(), email);
238
+    sendData(commands.EMAIL, email);
239
+}
240
+
241
+
242
+/**
243
+ * Changes the local avatar url for the local user
244
+ * @param avatarUrl {string} the new avatar url
245
+ */
246
+function changeLocalAvatarUrl(avatarUrl = '') {
247
+    avatarUrl = avatarUrl.trim();
248
+
249
+    if (avatarUrl === APP.settings.getAvatarUrl()) {
250
+        return;
251
+    }
252
+
253
+    APP.settings.setAvatarUrl(avatarUrl);
254
+    APP.UI.setUserAvatarUrl(room.myUserId(), avatarUrl);
255
+    sendData(commands.AVATAR_URL, avatarUrl);
256
+}
257
+
258
+/**
259
+ * Changes the display name for the local user
260
+ * @param nickname {string} the new display name
261
+ */
262
+function changeLocalDisplayName(nickname = '') {
263
+    nickname = nickname.trim();
264
+
265
+    if (nickname === APP.settings.getDisplayName()) {
266
+        return;
267
+    }
268
+
269
+    APP.settings.setDisplayName(nickname);
270
+    room.setDisplayName(nickname);
271
+    APP.UI.changeDisplayName(APP.conference.localId, nickname);
272
+}
273
+
212 274
 class ConferenceConnector {
213 275
     constructor(resolve, reject) {
214 276
         this._resolve = resolve;
@@ -227,6 +289,7 @@ class ConferenceConnector {
227 289
     }
228 290
     _onConferenceFailed(err, ...params) {
229 291
         console.error('CONFERENCE FAILED:', err, ...params);
292
+        APP.UI.hideRingOverLay();
230 293
         switch (err) {
231 294
             // room is locked by the password
232 295
         case ConferenceErrors.PASSWORD_REQUIRED:
@@ -412,6 +475,9 @@ export default {
412 475
             this._createRoom(tracks);
413 476
             this.isDesktopSharingEnabled =
414 477
                 JitsiMeetJS.isDesktopSharingEnabled();
478
+            if(this.isDesktopSharingEnabled)
479
+                APP.API.addPostisMessageListener('toggle-share-screen',
480
+                    () => this.toggleScreenSharing());
415 481
 
416 482
             // if user didn't give access to mic or camera or doesn't have
417 483
             // them at all, we disable corresponding toolbar buttons
@@ -851,13 +917,7 @@ export default {
851 917
         /**
852 918
          * Known custom conference commands.
853 919
          */
854
-        defaults: {
855
-            CONNECTION_QUALITY: "stats",
856
-            EMAIL: "email",
857
-            ETHERPAD: "etherpad",
858
-            SHARED_VIDEO: "shared-video",
859
-            CUSTOM_ROLE: "custom-role"
860
-        },
920
+        defaults: commands,
861 921
         /**
862 922
          * Receives notifications from other participants about commands aka
863 923
          * custom events (sent by sendCommand or sendCommandOnce methods).
@@ -910,7 +970,11 @@ export default {
910 970
         this._room = room; // FIXME do not use this
911 971
 
912 972
         let email = APP.settings.getEmail();
913
-        email && sendEmail(this.commands.defaults.EMAIL, email);
973
+        email && sendData(this.commands.defaults.EMAIL, email);
974
+
975
+        let avatarUrl = APP.settings.getAvatarUrl();
976
+        avatarUrl && sendData(this.commands.defaults.AVATAR_URL,
977
+            avatarUrl);
914 978
 
915 979
         let nick = APP.settings.getDisplayName();
916 980
         if (config.useNicks && !nick) {
@@ -1093,6 +1157,10 @@ export default {
1093 1157
         // add local streams when joined to the conference
1094 1158
         room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
1095 1159
             APP.UI.mucJoined();
1160
+            APP.API.sendPostisMessage({
1161
+              method: 'video-conference-joined',
1162
+              params: {roomName: APP.conference.roomName}
1163
+            });
1096 1164
         });
1097 1165
 
1098 1166
         room.on(
@@ -1297,33 +1365,20 @@ export default {
1297 1365
             APP.UI.initEtherpad(value);
1298 1366
         });
1299 1367
 
1300
-        APP.UI.addListener(UIEvents.EMAIL_CHANGED, (email = '') => {
1301
-            email = email.trim();
1302
-
1303
-            if (email === APP.settings.getEmail()) {
1304
-                return;
1305
-            }
1306
-
1307
-            APP.settings.setEmail(email);
1308
-            APP.UI.setUserAvatar(room.myUserId(), email);
1309
-            sendEmail(this.commands.defaults.EMAIL, email);
1310
-        });
1311
-        room.addCommandListener(this.commands.defaults.EMAIL, (data) => {
1312
-            APP.UI.setUserAvatar(data.attributes.id, data.value);
1368
+        APP.UI.addListener(UIEvents.EMAIL_CHANGED, changeLocalEmail);
1369
+        room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
1370
+            APP.UI.setUserEmail(from, data.value);
1313 1371
         });
1314 1372
 
1315
-        APP.UI.addListener(UIEvents.NICKNAME_CHANGED, (nickname = '') => {
1316
-            nickname = nickname.trim();
1373
+        APP.UI.addListener(UIEvents.AVATAR_URL_CHANGED, changeLocalAvatarUrl);
1317 1374
 
1318
-            if (nickname === APP.settings.getDisplayName()) {
1319
-                return;
1320
-            }
1321
-
1322
-            APP.settings.setDisplayName(nickname);
1323
-            room.setDisplayName(nickname);
1324
-            APP.UI.changeDisplayName(APP.conference.localId, nickname);
1375
+        room.addCommandListener(this.commands.defaults.AVATAR_URL,
1376
+        (data, from) => {
1377
+            APP.UI.setUserAvatarUrl(from, data.value);
1325 1378
         });
1326 1379
 
1380
+        APP.UI.addListener(UIEvents.NICKNAME_CHANGED, changeLocalDisplayName);
1381
+
1327 1382
         APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
1328 1383
             (startAudioMuted, startVideoMuted) => {
1329 1384
                 room.setStartMutedPolicy({

+ 7
- 5
connection_optimization/do_external_connect.js Visa fil

@@ -16,12 +16,13 @@
16 16
  * Executes createConnectionExternally function.
17 17
  */
18 18
 (function () {
19
-    var params = getConfigParamsFromUrl();
20
-    
19
+    var hashParams = getConfigParamsFromUrl("hash", true);
20
+    var searchParams = getConfigParamsFromUrl("search", true);
21
+
21 22
     //Url params have higher proirity than config params
22 23
     var url = config.externalConnectUrl;
23
-    if(params.hasOwnProperty('config.externalConnectUrl'))
24
-        url = params["config.externalConnectUrl"];
24
+    if(hashParams.hasOwnProperty('config.externalConnectUrl'))
25
+        url = hashParams["config.externalConnectUrl"];
25 26
 
26 27
     /**
27 28
      * Check if connect from connection.js was executed and executes the handler
@@ -57,7 +58,8 @@
57 58
 
58 59
     url += "?room=" + room_name;
59 60
 
60
-    var token = params["config.token"] || config.token;
61
+    var token = hashParams["config.token"] || config.token ||
62
+        searchParams.jwt;
61 63
     if(token)
62 64
         url += "&token=" + token;
63 65
 

+ 4
- 0
css/main.css Visa fil

@@ -33,6 +33,10 @@ html, body{
33 33
     display:none;
34 34
 }
35 35
 
36
+#header_container {
37
+    z-index: 1014;
38
+}
39
+
36 40
 .toolbar_span {
37 41
     display: inline-block;
38 42
     position: relative;

+ 51
- 0
css/overlay.css Visa fil

@@ -0,0 +1,51 @@
1
+
2
+.overlay {
3
+    position: fixed;
4
+    left: 0;
5
+    top: 0;
6
+    width: 100%;
7
+    height: 100%;
8
+    z-index: 1013;
9
+    background: #000000; /* Old browsers */
10
+    opacity: 0.75;
11
+    display: block;
12
+}
13
+
14
+.overlay_container {
15
+    width: 100%;
16
+    height: 100%;
17
+    position: fixed;
18
+    z-index: 1013;
19
+}
20
+
21
+.overlay_content {
22
+    color: #fff;
23
+    font-weight: normal;
24
+    font-size: 20px;
25
+    text-align: center;
26
+    width: 400px;
27
+    height: 250px;
28
+    top: 50%;
29
+    left: 50%;
30
+    position:absolute;
31
+    margin-top: -125px;
32
+    margin-left: -200px;
33
+}
34
+
35
+.overlay_avatar {
36
+    width: 200px;
37
+    height: 200px;
38
+    position: relative;
39
+    border-radius: 100px;
40
+    z-index: 1013;
41
+    float: left;
42
+    margin-left: 100px;
43
+}
44
+
45
+.overlay_text {
46
+    position: relative;
47
+    width: 400px;
48
+    z-index: 1013;
49
+    margin-top: 20px;
50
+    float: left;
51
+}

+ 2
- 2
css/popover.css Visa fil

@@ -2,7 +2,7 @@
2 2
   position: absolute;
3 3
   top: 0;
4 4
   left: 0;
5
-  z-index: 1010;
5
+  z-index: 1015;
6 6
   display: none;
7 7
   max-width: 300px;
8 8
   min-width: 100px;
@@ -121,4 +121,4 @@
121 121
   border-right-width: 0;
122 122
   border-left-color: #ffffff;
123 123
   bottom: -10px;
124
-}
124
+}

+ 1
- 1
css/toastr.css Visa fil

@@ -84,7 +84,7 @@ button.toast-close-button {
84 84
 }
85 85
 #toast-container {
86 86
   position: fixed;
87
-  z-index: 999999;
87
+  z-index: 1012;
88 88
   /*overrides*/
89 89
 
90 90
 }

+ 4
- 4
css/videolayout_default.css Visa fil

@@ -468,7 +468,7 @@
468 468
     position: absolute;
469 469
     width: 100%;
470 470
     top:50%;
471
-    z-index: 10000;
471
+    z-index: 1011;
472 472
     font-weight: 600;
473 473
     font-size: 14px;
474 474
     text-align: center;
@@ -488,7 +488,7 @@
488 488
     background: rgba(0,0,0,.5);
489 489
     padding: 10px;
490 490
     color: rgba(255,255,255,.5);
491
-    z-index: 10000;
491
+    z-index: 1011;
492 492
 }
493 493
 
494 494
 .centeredVideoLabel {
@@ -506,7 +506,7 @@
506 506
     margin-left: auto;
507 507
     background: rgba(0,0,0,.5);
508 508
     color: #FFF;
509
-    z-index: 10000;
509
+    z-index: 1011;
510 510
     border-radius: 2px;
511 511
     -webkit-transition: all 2s 2s linear;
512 512
     transition: all 2s 2s linear;
@@ -522,4 +522,4 @@
522 522
 }
523 523
 
524 524
 .hidden {
525
-}
525
+}

+ 3
- 3
debian/jitsi-meet-tokens.postinst Visa fil

@@ -67,8 +67,8 @@ case "$1" in
67 67
                 sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
68 68
 
69 69
                 # Install luajwt
70
-                if ! luarocks install luajwt; then
71
-                   echo "Failed to install luajwt - try installing it manually"
70
+                if ! luarocks install jwt; then
71
+                   echo "Failed to install jwt - try installing it manually"
72 72
                 fi
73 73
 
74 74
                 if [ -x "/etc/init.d/prosody" ]; then
@@ -85,7 +85,7 @@ case "$1" in
85 85
         else
86 86
             echo "Prosody config not found at $PROSODY_HOST_CONFIG - unable to auto-configure token authentication"
87 87
         fi
88
-	
88
+
89 89
     ;;
90 90
 
91 91
     abort-upgrade|abort-remove|abort-deconfigure)

+ 1
- 0
index.html Visa fil

@@ -241,6 +241,7 @@
241 241
             <div class="arrow-up"></div>
242 242
             <input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
243 243
             <input type="text" id="setEmail" placeholder="E-Mail">
244
+            <input type="text" id="setAvatarUrl" placeholder="Avatar URL" data-i18n="[placeholder]settings.avatar">
244 245
             <select id="languages_selectbox"></select>
245 246
             <div id = "startMutedOptions">
246 247
                 <label class = "startMutedLabel">

+ 3
- 1
lang/main.json Visa fil

@@ -10,6 +10,7 @@
10 10
     "speaker": "Speaker",
11 11
     "defaultNickname": "ex. Jane Pink",
12 12
     "defaultLink": "e.g. __url__",
13
+    "calling": "Calling __name__ ...",
13 14
     "welcomepage":{
14 15
         "go": "GO",
15 16
         "roomname": "Enter room name",
@@ -94,7 +95,8 @@
94 95
         "selectAudioOutput": "Select audio output",
95 96
         "followMe": "Enable follow me",
96 97
         "noDevice": "None",
97
-        "noPermission": "Permission to use device is not granted"
98
+        "noPermission": "Permission to use device is not granted",
99
+        "avatarUrl": "Avatar URL"
98 100
     },
99 101
     "videothumbnail":
100 102
     {

+ 93
- 32
modules/API/API.js Visa fil

@@ -5,6 +5,8 @@
5 5
  * applications that embed Jitsi Meet
6 6
  */
7 7
 
8
+ import postis from 'postis';
9
+
8 10
 /**
9 11
  * List of the available commands.
10 12
  * @type {{
@@ -16,7 +18,28 @@
16 18
  *              toggleContactList: toggleContactList
17 19
  *          }}
18 20
  */
19
-var commands = {};
21
+let commands = {};
22
+
23
+/**
24
+ * Object that will execute sendMessage
25
+ */
26
+let target = window.opener ? window.opener : window.parent;
27
+
28
+/**
29
+ * Array of functions that are going to receive the objects passed to this
30
+ * window
31
+ */
32
+let messageListeners = [];
33
+
34
+/**
35
+ * Current status (enabled/disabled) of Postis.
36
+ */
37
+let enablePostis = false;
38
+
39
+/**
40
+ * Current status (enabled/disabled) of Post Message API.
41
+ */
42
+let enablePostMessage = false;
20 43
 
21 44
 function initCommands() {
22 45
     commands = {
@@ -91,14 +114,6 @@ function processEvent(event) {
91 114
     }
92 115
 }
93 116
 
94
-/**
95
- * Sends message to the external application.
96
- * @param object
97
- */
98
-function sendMessage(object) {
99
-    window.parent.postMessage(JSON.stringify(object), "*");
100
-}
101
-
102 117
 /**
103 118
  * Processes a message event from the external application
104 119
  * @param event the message event
@@ -107,10 +122,11 @@ function processMessage(event) {
107 122
     var message;
108 123
     try {
109 124
         message = JSON.parse(event.data);
110
-    } catch (e) {}
111
-
112
-    if(!message.type)
125
+    } catch (e) {
126
+        console.error("Cannot parse data", event.data);
113 127
         return;
128
+    }
129
+
114 130
     switch (message.type) {
115 131
         case "command":
116 132
             processCommand(message);
@@ -119,18 +135,26 @@ function processMessage(event) {
119 135
             processEvent(message);
120 136
             break;
121 137
         default:
122
-            console.error("Unknown type of the message");
123
-            return;
138
+            console.warn("Unknown message type");
124 139
     }
125 140
 }
126 141
 
142
+/**
143
+ * Sends message to the external application.
144
+ * @param object {object} the object that will be sent as JSON string
145
+ */
146
+function sendMessage(object) {
147
+    if(enablePostMessage)
148
+        target.postMessage(JSON.stringify(object), "*");
149
+}
150
+
127 151
 /**
128 152
  * Check whether the API should be enabled or not.
129 153
  * @returns {boolean}
130 154
  */
131 155
 function isEnabled () {
132 156
     let hash = location.hash;
133
-    return hash && hash.indexOf("external=true") > -1 && window.postMessage;
157
+    return !!(hash && hash.indexOf("external=true") > -1 && window.postMessage);
134 158
 }
135 159
 
136 160
 /**
@@ -149,7 +173,7 @@ function isEventEnabled (name) {
149 173
  * @param object data associated with the event
150 174
  */
151 175
 function triggerEvent (name, object) {
152
-    if (isEnabled() && isEventEnabled(name)) {
176
+    if (isEventEnabled(name) && enablePostMessage) {
153 177
         sendMessage({
154 178
             type: "event",
155 179
             action: "result",
@@ -165,18 +189,34 @@ export default {
165 189
      * receive information from external applications that embed Jitsi Meet.
166 190
      * It also sends a message to the external application that APIConnector
167 191
      * is initialized.
192
+     * @param options {object}
193
+     * @param enablePostis {boolean} if true the postis npm
194
+     * package for comminication with the parent window will be enabled.
195
+     * @param enablePostMessage {boolean} if true the postMessageAPI for
196
+     * comminication with the parent window will be enabled.
168 197
      */
169
-    init: function () {
170
-        if (!isEnabled()) {
198
+    init: function (options = {}) {
199
+        options.enablePostMessage = options.enablePostMessage || isEnabled();
200
+        if (!options.enablePostis &&
201
+            !options.enablePostMessage) {
171 202
             return;
172 203
         }
173
-        initCommands();
174
-        if (window.addEventListener) {
175
-            window.addEventListener('message', processMessage, false);
176
-        } else {
177
-            window.attachEvent('onmessage', processMessage);
204
+        enablePostis = options.enablePostis;
205
+        enablePostMessage = options.enablePostMessage;
206
+
207
+        if(enablePostMessage) {
208
+            initCommands();
209
+            if (window.addEventListener) {
210
+                window.addEventListener('message', processMessage, false);
211
+            } else {
212
+                window.attachEvent('onmessage', processMessage);
213
+            }
214
+            sendMessage({type: "system", loaded: true});
215
+        }
216
+
217
+        if(enablePostis) {
218
+            this.postis = postis({window: target});
178 219
         }
179
-        sendMessage({type: "system", loaded: true});
180 220
     },
181 221
 
182 222
     /**
@@ -187,6 +227,27 @@ export default {
187 227
         triggerEvent("outgoingMessage", {"message": body});
188 228
     },
189 229
 
230
+    /**
231
+     * Sends message to the external application.
232
+     * @param options {object}
233
+     * @param method {string}
234
+     * @param params {object} the object that will be sent as JSON string
235
+     */
236
+    sendPostisMessage(options) {
237
+        if(enablePostis)
238
+            this.postis.send(options);
239
+    },
240
+
241
+    /**
242
+     * Adds listener for Postis messages.
243
+     * @param method {string} postis mehtod
244
+     * @param listener {function}
245
+     */
246
+    addPostisMessageListener (method, listener) {
247
+        if(enablePostis)
248
+            this.postis.listen(method, listener);
249
+    },
250
+
190 251
     /**
191 252
      * Notify external application (if API is enabled) that
192 253
      * message was received.
@@ -238,14 +299,14 @@ export default {
238 299
      * Removes the listeners.
239 300
      */
240 301
     dispose: function () {
241
-        if (!isEnabled()) {
242
-            return;
243
-        }
244
-
245
-        if (window.removeEventListener) {
246
-            window.removeEventListener("message", processMessage, false);
247
-        } else {
248
-            window.detachEvent('onmessage', processMessage);
302
+        if (enablePostMessage) {
303
+            if (window.removeEventListener) {
304
+                window.removeEventListener("message", processMessage, false);
305
+            } else {
306
+                window.detachEvent('onmessage', processMessage);
307
+            }
249 308
         }
309
+        if(enablePostis)
310
+            this.postis.destroy();
250 311
     }
251 312
 };

+ 116
- 0
modules/TokenData/TokenData.js Visa fil

@@ -0,0 +1,116 @@
1
+/* global  getConfigParamsFromUrl, config */
2
+
3
+/**
4
+ * Parses and handles JWT tokens. Sets config.token.
5
+ */
6
+
7
+import * as jws from "jws";
8
+
9
+/**
10
+ * Get the JWT token from the URL.
11
+ */
12
+let params = getConfigParamsFromUrl("search", true);
13
+let jwt = params.jwt;
14
+
15
+/**
16
+ * Implements a user of conference.
17
+ */
18
+class User {
19
+    /**
20
+     * @param name {string} the name of the user.
21
+     * @param email {string} the email of the user.
22
+     * @param avatarUrl {string} the URL for the avatar of the user.
23
+     */
24
+    constructor(name, email, avatarUrl) {
25
+        this._name = name;
26
+        this._email = email;
27
+        this._avatarUrl = avatarUrl;
28
+    }
29
+
30
+    /**
31
+     * GETERS START.
32
+     */
33
+
34
+    /**
35
+     * Returns the name property
36
+     */
37
+    getName() {
38
+        return this._name;
39
+    }
40
+
41
+    /**
42
+     * Returns the email property
43
+     */
44
+    getEmail() {
45
+        return this._email;
46
+    }
47
+
48
+    /**
49
+     * Returns the URL of the avatar
50
+     */
51
+    getAvatarUrl() {
52
+        return this._avatarUrl;
53
+    }
54
+
55
+    /**
56
+     * GETERS END.
57
+     */
58
+}
59
+
60
+/**
61
+ * Represent the data parsed from the JWT token
62
+ */
63
+class TokenData{
64
+    /**
65
+     * @param {string} the JWT token
66
+     */
67
+    constructor(jwt) {
68
+        if(!jwt)
69
+            return;
70
+        //Use jwt param as token if there is not other token set
71
+        if(!config.token)
72
+            config.token = jwt;
73
+        this.jwt = jwt;
74
+
75
+        //External API settings
76
+        this.externalAPISettings = {
77
+            enablePostis: true
78
+        };
79
+        this._decode();
80
+    }
81
+
82
+    /**
83
+     * Decodes the JWT token and sets the decoded data to properties.
84
+     */
85
+    _decode() {
86
+        this.decodedJWT = jws.decode(jwt);
87
+        if(!this.decodedJWT || !this.decodedJWT.payload)
88
+            return;
89
+        this.payload = this.decodedJWT.payload;
90
+        if(!this.payload.context)
91
+            return;
92
+        let callerData = this.payload.context.user;
93
+        let calleeData = this.payload.context.callee;
94
+        if(callerData)
95
+            this.caller = new User(callerData.name, callerData.email,
96
+                callerData.avatarUrl);
97
+        if(calleeData)
98
+            this.callee = new User(calleeData.name, calleeData.email,
99
+                calleeData.avatarUrl);
100
+    }
101
+}
102
+
103
+/**
104
+ * Stores the TokenData instance.
105
+ */
106
+let data = null;
107
+
108
+/**
109
+ * Returns the data variable. Creates new TokenData instance if <tt>data</tt>
110
+ * variable is null.
111
+ */
112
+export default function getTokenData() {
113
+    if(!data)
114
+        data = new TokenData(jwt);
115
+    return data;
116
+}

+ 52
- 11
modules/UI/UI.js Visa fil

@@ -21,6 +21,7 @@ import FilmStrip from "./videolayout/FilmStrip";
21 21
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
22 22
 import Settings from "./../settings/Settings";
23 23
 import { reload } from '../util/helpers';
24
+import RingOverlay from "./ring_overlay/RingOverlay";
24 25
 
25 26
 var EventEmitter = require("events");
26 27
 UI.messageHandler = require("./util/MessageHandler");
@@ -277,7 +278,7 @@ UI.initConference = function () {
277 278
     }
278 279
 
279 280
     // Make sure we configure our avatar id, before creating avatar for us
280
-    UI.setUserAvatar(id, Settings.getEmail());
281
+    UI.setUserEmail(id, Settings.getEmail());
281 282
 
282 283
     Toolbar.checkAutoEnableDesktopSharing();
283 284
 
@@ -470,6 +471,10 @@ UI.start = function () {
470 471
         SettingsMenu.init(eventEmitter);
471 472
     }
472 473
 
474
+    if(APP.tokenData.callee) {
475
+        UI.showRingOverLay();
476
+    }
477
+
473 478
     // Return true to indicate that the UI has been fully started and
474 479
     // conference ready.
475 480
     return true;
@@ -551,6 +556,7 @@ UI.getSharedDocumentManager = function () {
551 556
  * @param {string} displayName user nickname
552 557
  */
553 558
 UI.addUser = function (id, displayName) {
559
+    UI.hideRingOverLay();
554 560
     ContactList.addContact(id);
555 561
 
556 562
     messageHandler.notify(
@@ -565,7 +571,7 @@ UI.addUser = function (id, displayName) {
565 571
     VideoLayout.addParticipantContainer(id);
566 572
 
567 573
     // Configure avatar
568
-    UI.setUserAvatar(id);
574
+    UI.setUserEmail(id);
569 575
 
570 576
     // set initial display name
571 577
     if(displayName)
@@ -800,21 +806,41 @@ UI.dockToolbar = function (isDock) {
800 806
 };
801 807
 
802 808
 /**
803
- * Update user avatar.
809
+ * Updates the avatar for participant.
804 810
  * @param {string} id user id
805
- * @param {stirng} email user email
811
+ * @param {stirng} avatarUrl the URL for the avatar
806 812
  */
807
-UI.setUserAvatar = function (id, email) {
808
-    // update avatar
809
-    Avatar.setUserAvatar(id, email);
810
-
811
-    var avatarUrl = Avatar.getAvatarUrl(id);
812
-
813
+function changeAvatar(id, avatarUrl) {
813 814
     VideoLayout.changeUserAvatar(id, avatarUrl);
814 815
     ContactList.changeUserAvatar(id, avatarUrl);
815 816
     if (APP.conference.isLocalId(id)) {
816 817
         SettingsMenu.changeAvatar(avatarUrl);
817 818
     }
819
+}
820
+
821
+/**
822
+ * Update user email.
823
+ * @param {string} id user id
824
+ * @param {stirng} email user email
825
+ */
826
+UI.setUserEmail = function (id, email) {
827
+    // update avatar
828
+    Avatar.setUserEmail(id, email);
829
+
830
+    changeAvatar(id, Avatar.getAvatarUrl(id));
831
+};
832
+
833
+
834
+/**
835
+ * Update user avatar URL.
836
+ * @param {string} id user id
837
+ * @param {stirng} url user avatar url
838
+ */
839
+UI.setUserAvatarUrl = function (id, url) {
840
+    // update avatar
841
+    Avatar.setUserAvatarUrl(id, url);
842
+
843
+    changeAvatar(id, Avatar.getAvatarUrl(id));
818 844
 };
819 845
 
820 846
 /**
@@ -1216,7 +1242,7 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
1216 1242
             : ``;
1217 1243
     let doNotShowWarningAgainSection = showDoNotShowWarning
1218 1244
         ? `<label>
1219
-            <input type='checkbox' id='doNotShowWarningAgain'> 
1245
+            <input type='checkbox' id='doNotShowWarningAgain'>
1220 1246
             <span data-i18n='dialog.doNotShowWarningAgain'></span>
1221 1247
            </label>`
1222 1248
         : ``;
@@ -1343,4 +1369,19 @@ UI.enableMicrophoneButton = function () {
1343 1369
     Toolbar.markAudioIconAsDisabled(false);
1344 1370
 };
1345 1371
 
1372
+let bottomToolbarEnabled = null;
1373
+
1374
+UI.showRingOverLay = function () {
1375
+    RingOverlay.show(APP.tokenData.callee);
1376
+    ToolbarToggler.setAlwaysVisibleToolbar(true);
1377
+    FilmStrip.toggleFilmStrip(false);
1378
+};
1379
+
1380
+UI.hideRingOverLay = function () {
1381
+    if(!RingOverlay.hide())
1382
+        return;
1383
+    ToolbarToggler.resetAlwaysVisibleToolbar();
1384
+    FilmStrip.toggleFilmStrip(true);
1385
+};
1386
+
1346 1387
 module.exports = UI;

+ 34
- 8
modules/UI/avatar/Avatar.js Visa fil

@@ -3,6 +3,19 @@
3 3
 let users = {};
4 4
 
5 5
 export default {
6
+    /**
7
+     * Sets prop in users object.
8
+     * @param id {string} user id
9
+     * @param prop {string} name of the prop
10
+     * @param val {string} value to be set
11
+     */
12
+    _setUserProp: function (id, prop, val) {
13
+        if(!val || (users[id] && users[id][prop] === val))
14
+            return;
15
+        if(!users[id])
16
+            users[id] = {};
17
+        users[id][prop] = val;
18
+    },
6 19
 
7 20
     /**
8 21
      * Sets the user's avatar in the settings menu(if local user), contact list
@@ -10,13 +23,18 @@ export default {
10 23
      * @param id id of the user
11 24
      * @param email email or nickname to be used as a hash
12 25
      */
13
-    setUserAvatar: function (id, email) {
14
-        if (email) {
15
-            if (users[id] === email) {
16
-                return;
17
-            }
18
-            users[id] = email;
19
-        }
26
+    setUserEmail: function (id, email) {
27
+        this._setUserProp(id, "email", email);
28
+    },
29
+
30
+    /**
31
+     * Sets the user's avatar in the settings menu(if local user), contact list
32
+     * and thumbnail
33
+     * @param id id of the user
34
+     * @param url the url for the avatar
35
+     */
36
+    setUserAvatarUrl: function (id, url) {
37
+        this._setUserProp(id, "url", url);
20 38
     },
21 39
 
22 40
     /**
@@ -34,7 +52,15 @@ export default {
34 52
             return null;
35 53
         }
36 54
 
37
-        let avatarId = users[userId];
55
+        let avatarId = null;
56
+        const user = users[userId];
57
+
58
+        if(user) {
59
+            if(user.url)
60
+                return users[userId].url;
61
+
62
+            avatarId = users[userId].email;
63
+        }
38 64
 
39 65
         // If the ID looks like an email, we'll use gravatar.
40 66
         // Otherwise, it's a random avatar, and we'll use the configured

+ 82
- 0
modules/UI/ring_overlay/RingOverlay.js Visa fil

@@ -0,0 +1,82 @@
1
+/* global $ */
2
+
3
+/**
4
+ * Shows ring overlay
5
+ */
6
+class RingOverlay {
7
+    /**
8
+     * @param callee instance of User class from TokenData.js
9
+     */
10
+    constructor(callee) {
11
+        this.callee = callee;
12
+        this._buildHtml();
13
+        this.audio = $("#ring_overlay_ringing");
14
+        this.audio[0].play();
15
+        this._setAudioTimeout();
16
+    }
17
+
18
+    /**
19
+     * Builds and appends the ring overlay to the html document
20
+     */
21
+    _buildHtml() {
22
+        $("body").append("<div class='overlay_container' >" +
23
+        "<div class='overlay' /><div class='overlay_content'>" +
24
+        "<img class='overlay_avatar' src='" +
25
+        this.callee.getAvatarUrl() + "' />" +
26
+        "<span data-i18n='calling' data-i18n-options='" +
27
+        JSON.stringify({name: this.callee.getName()}) +
28
+        "' class='overlay_text'>Calling " +
29
+        this.callee.getName() + "...</span></div>" +
30
+        "<audio id='ring_overlay_ringing' src='/sounds/ring.ogg' /></div>");
31
+    }
32
+
33
+    /**
34
+     * Sets the interval that is going to play the ringing sound.
35
+     */
36
+    _setAudioTimeout() {
37
+        this.interval = setInterval( () => {
38
+            this.audio[0].play();
39
+        }, 5000);
40
+    }
41
+
42
+    /**
43
+     * Destroys and clears all the objects (html elements and audio interval)
44
+     * related to the ring overlay.
45
+     */
46
+    destroy() {
47
+        if(this.interval)
48
+            clearInterval(this.interval);
49
+        $(".overlay_container").remove();
50
+    }
51
+}
52
+
53
+/**
54
+ * Store the current ring overlay instance.
55
+ * Note: We want to have only 1 instance at a time.
56
+ */
57
+let overlay = null;
58
+
59
+export default {
60
+    /**
61
+     * Shows the ring overlay for the passed callee.
62
+     * @param callee {class User} the callee. Instance of User class from
63
+     * TokenData.js
64
+     */
65
+    show(callee) {
66
+        if(overlay) {
67
+            this.hide();
68
+        }
69
+        overlay = new RingOverlay(callee);
70
+    },
71
+    /**
72
+     * Hides the ring overlay. Destroys all the elements related to the ring
73
+     * overlay.
74
+     */
75
+    hide() {
76
+        if(!overlay)
77
+            return false;
78
+        overlay.destroy();
79
+        overlay = null;
80
+        return true;
81
+    }
82
+};

+ 14
- 0
modules/UI/side_pannels/settings/SettingsMenu.js Visa fil

@@ -81,6 +81,12 @@ export default {
81 81
         function updateEmail () {
82 82
             emitter.emit(UIEvents.EMAIL_CHANGED, $('#setEmail').val());
83 83
         }
84
+
85
+        // AVATAR URL CHANGED
86
+        function updateAvatarUrl () {
87
+            emitter.emit(UIEvents.AVATAR_URL_CHANGED, $('#setAvatarUrl').val());
88
+        }
89
+
84 90
         $('#setEmail')
85 91
             .val(Settings.getEmail())
86 92
             .keyup(function (event) {
@@ -89,6 +95,14 @@ export default {
89 95
             }
90 96
         }).focusout(updateEmail);
91 97
 
98
+        $('#setAvatarUrl')
99
+            .val(Settings.getAvatarUrl())
100
+            .keyup(function (event) {
101
+            if (event.keyCode === 13) { // enter
102
+                updateAvatarUrl();
103
+            }
104
+        }).focusout(updateAvatarUrl);
105
+
92 106
 
93 107
         // START MUTED
94 108
         $("#startMutedOptions").change(function () {

+ 18
- 1
modules/UI/toolbars/ToolbarToggler.js Visa fil

@@ -7,6 +7,10 @@ import FilmStrip from '../videolayout/FilmStrip.js';
7 7
 
8 8
 let toolbarTimeoutObject;
9 9
 let toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
10
+/**
11
+ * If true the toolbar will be always displayed
12
+ */
13
+let alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
10 14
 
11 15
 function showDesktopSharingButton() {
12 16
     if (APP.conference.isDesktopSharingEnabled &&
@@ -21,7 +25,7 @@ function showDesktopSharingButton() {
21 25
  * Hides the toolbar.
22 26
  */
23 27
 function hideToolbar() {
24
-    if (config.alwaysVisibleToolbar) {
28
+    if (alwaysVisibleToolbar) {
25 29
         return;
26 30
     }
27 31
 
@@ -40,6 +44,19 @@ function hideToolbar() {
40 44
 }
41 45
 
42 46
 const ToolbarToggler = {
47
+    /**
48
+     * Sets the value of alwaysVisibleToolbar variable.
49
+     * @param value {boolean} the new value of alwaysVisibleToolbar variable
50
+     */
51
+    setAlwaysVisibleToolbar(value) {
52
+        alwaysVisibleToolbar = value;
53
+    },
54
+    /**
55
+     * Resets the value of alwaysVisibleToolbar variable to the default one.
56
+     */
57
+    resetAlwaysVisibleToolbar() {
58
+        alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
59
+    },
43 60
     /**
44 61
      * Shows the main toolbar.
45 62
      */

+ 20
- 0
modules/settings/Settings.js Visa fil

@@ -9,6 +9,7 @@ let cameraDeviceId = '';
9 9
 let micDeviceId = '';
10 10
 let welcomePageDisabled = false;
11 11
 let localFlipX = null;
12
+let avatarUrl = '';
12 13
 
13 14
 function supportsLocalStorage() {
14 15
     try {
@@ -34,6 +35,7 @@ if (supportsLocalStorage()) {
34 35
     }
35 36
 
36 37
     email = UIUtil.unescapeHtml(window.localStorage.email || '');
38
+    avatarUrl = UIUtil.unescapeHtml(window.localStorage.avatarUrl || '');
37 39
     localFlipX = JSON.parse(window.localStorage.localFlipX || true);
38 40
     displayName = UIUtil.unescapeHtml(window.localStorage.displayname || '');
39 41
     language = window.localStorage.language;
@@ -98,6 +100,24 @@ export default {
98 100
         return email;
99 101
     },
100 102
 
103
+    /**
104
+     * Sets new avatarUrl for local user and saves it to the local storage.
105
+     * @param {string} newAvatarUrl new avatarUrl for the local user
106
+     */
107
+    setAvatarUrl: function (newAvatarUrl) {
108
+        avatarUrl = newAvatarUrl;
109
+        window.localStorage.avatarUrl = UIUtil.escapeHtml(newAvatarUrl);
110
+    },
111
+
112
+    /**
113
+     * Returns avatarUrl address of the local user.
114
+     * @returns {string} avatarUrl
115
+     */
116
+    getAvatarUrl: function () {
117
+        return avatarUrl;
118
+    },
119
+
120
+
101 121
     getLanguage () {
102 122
         return language;
103 123
     },

+ 4
- 2
package.json Visa fil

@@ -29,8 +29,10 @@
29 29
     "jssha": "1.5.0",
30 30
     "retry": "0.6.1",
31 31
     "strophe": "^1.2.2",
32
-    "strophejs-plugins": "^0.0.6",
33
-    "toastr": "^2.0.3"
32
+    "strophejs-plugins": "strophe/strophejs-plugins",
33
+    "toastr": "^2.0.3",
34
+    "postis": "^2.2.0",
35
+    "jws": "*"
34 36
   },
35 37
   "devDependencies": {
36 38
     "browserify": "11.1.x",

+ 3
- 3
prosody-plugins/mod_auth_token.lua Visa fil

@@ -15,6 +15,7 @@ local host = module.host;
15 15
 local appId = module:get_option_string("app_id");
16 16
 local appSecret = module:get_option_string("app_secret");
17 17
 local allowEmptyToken = module:get_option_boolean("allow_empty_token");
18
+local disableRoomNameConstraints = module:get_option_boolean("disable_room_name_constraints");
18 19
 
19 20
 if allowEmptyToken == true then
20 21
 	module:log("warn", "WARNING - empty tokens allowed");
@@ -79,7 +80,7 @@ function provider.get_sasl_handler(session)
79 80
 
80 81
 		-- here we check if 'room' claim exists
81 82
 		local room, roomErr = token_util.get_room_name(token, appSecret);
82
-		if room == nil then
83
+		if room == nil and disableRoomNameConstraints ~= true then
83 84
             if roomErr == nil then
84 85
                 roomErr = "'room' claim is missing";
85 86
             end
@@ -88,7 +89,7 @@ function provider.get_sasl_handler(session)
88 89
 
89 90
 		-- now verify the whole token
90 91
 		local result, msg
91
-		= token_util.verify_token(token, appId, appSecret, room);
92
+		= token_util.verify_token(token, appId, appSecret, room, disableRoomNameConstraints);
92 93
 		if result == true then
93 94
 			-- Binds room name to the session which is later checked on MUC join
94 95
 			session.jitsi_meet_room = room;
@@ -121,4 +122,3 @@ local function anonymous(self, message)
121 122
 end
122 123
 
123 124
 sasl.registerMechanism("ANONYMOUS", {"anonymous"}, anonymous);
124
-

+ 9
- 9
prosody-plugins/mod_token_verification.lua Visa fil

@@ -24,6 +24,7 @@ end
24 24
 local appId = parentCtx:get_option_string("app_id");
25 25
 local appSecret = parentCtx:get_option_string("app_secret");
26 26
 local allowEmptyToken = parentCtx:get_option_boolean("allow_empty_token");
27
+local disableRoomNameConstraints = parentCtx:get_option_boolean("disable_room_name_constraints")
27 28
 
28 29
 log("debug",
29 30
 	"%s - starting MUC token verifier app_id: %s app_secret: %s allow empty: %s",
@@ -35,13 +36,6 @@ local function verify_user(session, stanza)
35 36
 		tostring(session.auth_token),
36 37
 		tostring(session.jitsi_meet_room));
37 38
 
38
-	if allowEmptyToken and session.auth_token == nil then
39
-		module:log(
40
-			"debug",
41
-			"Skipped room token verification - empty tokens are allowed");
42
-		return nil;
43
-	end
44
-
45 39
 	-- token not required for admin users
46 40
 	local user_jid = stanza.attr.from;
47 41
 	if is_admin(user_jid) then
@@ -49,6 +43,13 @@ local function verify_user(session, stanza)
49 43
 		return nil;
50 44
 	end
51 45
 
46
+	if allowEmptyToken and session.auth_token == nil then
47
+		module:log(
48
+			"debug",
49
+			"Skipped room token verification - empty tokens are allowed");
50
+		return nil;
51
+	end
52
+
52 53
 	local room = string.match(stanza.attr.to, "^(%w+)@");
53 54
 	log("debug", "Will verify token for user: %s, room: %s ", user_jid, room);
54 55
 	if room == nil then
@@ -59,7 +60,7 @@ local function verify_user(session, stanza)
59 60
 
60 61
 	local token = session.auth_token;
61 62
 	local auth_room = session.jitsi_meet_room;
62
-	if room ~= auth_room then
63
+	if room ~= auth_room and disableRoomNameConstraints ~= true then
63 64
 		log("error", "Token %s not allowed to join: %s",
64 65
 			tostring(token), tostring(auth_room));
65 66
 		session.send(
@@ -81,4 +82,3 @@ module:hook("muc-occupant-pre-join", function(event)
81 82
 	log("debug", "pre join: %s %s", tostring(room), tostring(stanza));
82 83
 	return verify_user(origin, stanza);
83 84
 end);
84
-

+ 7
- 7
prosody-plugins/token/util.lib.lua Visa fil

@@ -1,7 +1,7 @@
1 1
 -- Token authentication
2 2
 -- Copyright (C) 2015 Atlassian
3 3
 
4
-local jwt = require "luajwt";
4
+local jwt = require "jwt";
5 5
 
6 6
 local _M = {};
7 7
 
@@ -14,7 +14,7 @@ local function _get_room_name(token, appSecret)
14 14
 	end
15 15
 end
16 16
 
17
-local function _verify_token(token, appId, appSecret, roomName)
17
+local function _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
18 18
 
19 19
 	local claims, err = jwt.decode(token, appSecret, true);
20 20
 	if claims == nil then
@@ -30,22 +30,22 @@ local function _verify_token(token, appId, appSecret, roomName)
30 30
 	end
31 31
 
32 32
 	local roomClaim = claims["room"];
33
-	if roomClaim == nil then
33
+	if roomClaim == nil and disableRoomNameConstraints ~= true then
34 34
 		return nil, "'room' claim is missing";
35 35
 	end
36
-	if roomName ~= nil and roomName ~= roomClaim then
36
+	if roomName ~= nil and roomName ~= roomClaim and disableRoomNameConstraints ~= true then
37 37
 		return nil, "Invalid room name('room' claim)";
38 38
 	end
39 39
 
40 40
 	return true;
41 41
 end
42 42
 
43
-function _M.verify_token(token, appId, appSecret, roomName)
44
-	return _verify_token(token, appId, appSecret, roomName);
43
+function _M.verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
44
+	return _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints);
45 45
 end
46 46
 
47 47
 function _M.get_room_name(token, appSecret)
48 48
 	return _get_room_name(token, appSecret);
49 49
 end
50 50
 
51
-return _M;
51
+return _M;

+ 4
- 0
service/UI/UIEvents.js Visa fil

@@ -14,6 +14,10 @@ export default {
14 14
      * Notifies that local user changed email.
15 15
      */
16 16
     EMAIL_CHANGED: "UI.email_changed",
17
+    /**
18
+     * Notifies that local user changed avatar url.
19
+     */
20
+    AVATAR_URL_CHANGED: "UI.avatar_url_changed",
17 21
     /**
18 22
      * Notifies that "start muted" settings changed.
19 23
      */

Binär
sounds/ring.ogg Visa fil


Binär
sounds/ring.wav Visa fil


+ 22
- 7
utils.js Visa fil

@@ -36,17 +36,32 @@ function getRoomName () {
36 36
 }
37 37
 
38 38
 /**
39
- * Parses the hash parameters from the URL and returns them as a JS object.
39
+ * Parses the parameters from the URL and returns them as a JS object.
40
+ * @param source {string} values - "hash"/"search" if "search" the parameters
41
+ * will parsed from location.search otherwise from location.hash
42
+ * @param dontParse if false or undefined some transformations
43
+ * (for parsing the value as JSON) are going to be executed
40 44
  */
41
-function getConfigParamsFromUrl() {
42
-    if (!location.hash)
45
+function getConfigParamsFromUrl(source, dontParse) {
46
+    var paramStr = (source === "search")? location.search : location.hash;
47
+    if (!paramStr)
43 48
         return {};
44
-    var hash = location.hash.substr(1);
49
+    paramStr = paramStr.substr(1);
45 50
     var result = {};
46
-    hash.split("&").forEach(function (part) {
51
+    paramStr.split("&").forEach(function (part) {
47 52
         var item = part.split("=");
48
-        result[item[0]] = JSON.parse(
49
-            decodeURIComponent(item[1]).replace(/\\&/, "&"));
53
+        var value;
54
+        try {
55
+            value = (dontParse)? item[1] : JSON.parse(
56
+                decodeURIComponent(item[1]).replace(/\\&/, "&"));
57
+        } catch (e) {
58
+            console.warn("Failed to parse URL argument", e);
59
+            if(window.onerror)
60
+                window.onerror("Failed to parse URL argument", null, null,
61
+                    null, e);
62
+            return;
63
+        }
64
+        result[item[0]] = value;
50 65
     });
51 66
     return result;
52 67
 }

Laddar…
Avbryt
Spara