SEBSERV-148 testing fixing
This commit is contained in:
parent
d014dfe45a
commit
7fae3f4baf
10 changed files with 530 additions and 170 deletions
|
@ -74,6 +74,9 @@ public final class Constants {
|
|||
public static final String URL_ADDRESS_SEPARATOR = COLON.toString() + SLASH.toString() + SLASH.toString();
|
||||
public static final String URL_PATH_SEPARATOR = SLASH.toString();
|
||||
|
||||
public static final String DYN_HTML_ATTR_OPEN = "%%_";
|
||||
public static final String DYN_HTML_ATTR_CLOSE = "_%%";
|
||||
|
||||
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
public static final String TIME_ZONE_OFFSET_TAIL_FORMAT = "|ZZ";
|
||||
|
||||
|
|
|
@ -11,12 +11,16 @@ package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
|
@ -28,58 +32,21 @@ public class JitsiWindowScriptResolver implements ProctoringWindowScriptResolver
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(JitsiWindowScriptResolver.class);
|
||||
|
||||
private static final String ATTR_SUBJECT = "_subject_";
|
||||
private static final String ATTR_ACCESS_TOKEN = "_accessToken_";
|
||||
private static final String ATTR_ROOM_NAME = "_roomName_";
|
||||
private static final String ATTR_HOST = "_host_";
|
||||
static final String RES_PATH =
|
||||
"classpath:ch/ethz/seb/sebserver/gui/service/session/proctoring/jitsiWindow.html";
|
||||
|
||||
// @formatter:off
|
||||
private static final String JITSI_WINDOW_HTML =
|
||||
"<!DOCTYPE html>" +
|
||||
"<html>" +
|
||||
"<head>" +
|
||||
" <title></title>" +
|
||||
" <script src='https://%%_" + ATTR_HOST + "_%%/external_api.js'></script>" +
|
||||
"</head>" +
|
||||
"" +
|
||||
"<body>" +
|
||||
"<div id=\"proctoring\"></div> " +
|
||||
"</body>" +
|
||||
"<script>" +
|
||||
" const options = {\n" +
|
||||
" parentNode: document.querySelector('#proctoring'),\n" +
|
||||
" roomName: '%%_" + ATTR_ROOM_NAME + "_%%',\n" +
|
||||
// " width: window.innerWidth,\n" +
|
||||
" height: window.innerHeight - 4,\n" +
|
||||
" jwt: '%%_" + ATTR_ACCESS_TOKEN + "_%%',\n" +
|
||||
" configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: false, disable1On1Mode: true },\n" +
|
||||
" interfaceConfigOverwrite: { " +
|
||||
"TOOLBAR_BUTTONS: [\r\n" +
|
||||
" 'microphone', 'camera',\r\n" +
|
||||
" 'fodeviceselection', 'profile', 'chat', 'recording',\r\n" +
|
||||
" 'livestreaming', 'settings',\r\n" +
|
||||
" 'videoquality', 'filmstrip', 'feedback',\r\n" +
|
||||
" 'tileview', 'help', 'mute-everyone', 'security'\r\n" +
|
||||
" ],"
|
||||
+ "SHOW_WATERMARK_FOR_GUESTS: false, "
|
||||
+ "RECENT_LIST_ENABLED: false, "
|
||||
+ "HIDE_INVITE_MORE_HEADER: true, "
|
||||
+ "DISABLE_RINGING: true, "
|
||||
+ "DISABLE_PRESENCE_STATUS: true, "
|
||||
+ "DISABLE_JOIN_LEAVE_NOTIFICATIONS: true, "
|
||||
+ "GENERATE_ROOMNAMES_ON_WELCOME_PAGE: false, "
|
||||
+ "MOBILE_APP_PROMO: false, "
|
||||
+ "SHOW_JITSI_WATERMARK: false, "
|
||||
+ "DISABLE_PRESENCE_STATUS: true, "
|
||||
+ "DISABLE_RINGING: true, "
|
||||
+ "DISABLE_VIDEO_BACKGROUND: false, "
|
||||
+ "filmStripOnly: false }\n" +
|
||||
" }\n" +
|
||||
" const meetAPI = new JitsiMeetExternalAPI(\"%%_" + ATTR_HOST + "_%%\", options);\n" +
|
||||
" meetAPI.executeCommand('subject', '%%_" + ATTR_SUBJECT + "_%%');\n" +
|
||||
"</script>" +
|
||||
"</html>";
|
||||
// @formatter:on
|
||||
private static final String ATTR_SUBJECT = "ATTR_SUBJECT";
|
||||
private static final String ATTR_ACCESS_TOKEN = "ATTR_ACCESS_TOKEN";
|
||||
private static final String ATTR_ROOM_NAME = "ATTR_ROOM_NAME";
|
||||
private static final String ATTR_HOST = "ATTR_HOST";
|
||||
|
||||
private final Resource resourceFile;
|
||||
|
||||
public JitsiWindowScriptResolver(
|
||||
@Value(RES_PATH) final Resource resourceFile) {
|
||||
|
||||
this.resourceFile = resourceFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(final ProctoringWindowData data) {
|
||||
|
@ -93,14 +60,28 @@ public class JitsiWindowScriptResolver implements ProctoringWindowScriptResolver
|
|||
|
||||
@Override
|
||||
public String getProctoringWindowScript(final ProctoringWindowData data) {
|
||||
|
||||
final Map<String, String> args = new HashMap<>();
|
||||
args.put(ATTR_HOST, data.connectionData.serverHost);
|
||||
args.put(ATTR_ROOM_NAME, data.connectionData.roomName);
|
||||
args.put(ATTR_ACCESS_TOKEN, String.valueOf(data.connectionData.accessToken));
|
||||
args.put(ATTR_SUBJECT, data.connectionData.subject);
|
||||
|
||||
return new StringSubstitutor(args, "%%_", "_%%")
|
||||
.replace(JITSI_WINDOW_HTML);
|
||||
return new StringSubstitutor(
|
||||
args,
|
||||
Constants.DYN_HTML_ATTR_OPEN,
|
||||
Constants.DYN_HTML_ATTR_CLOSE)
|
||||
.replace(getHTMLWindow());
|
||||
|
||||
}
|
||||
|
||||
private String getHTMLWindow() {
|
||||
try {
|
||||
return IOUtils.toString(this.resourceFile.getInputStream());
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to load Jitsi Meet window resources", e);
|
||||
return "ERROR: " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,10 +10,24 @@ package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
|||
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
|
||||
/** Defines a proctoring window script resolver that generates the
|
||||
* proctoring window html and script code for a particular
|
||||
* proctoring service. */
|
||||
public interface ProctoringWindowScriptResolver {
|
||||
|
||||
/** Indicates if the concrete implementation applies to given proctoring data.
|
||||
* Usually this looks after the proctoring service type within the given data
|
||||
* and returns true if the implementation is compatible with the given proctoring
|
||||
* service type.
|
||||
*
|
||||
* @param data ProctoringWindowData instance containing actual proctoring data
|
||||
* @return true if a concrete implementation applies to the given data */
|
||||
boolean applies(ProctoringWindowData data);
|
||||
|
||||
/** Produces the html and java script page to open in a proctoring window pop-up.
|
||||
*
|
||||
* @param data ProctoringWindowData instance containing actual proctoring data
|
||||
* @return the html and java script page to open in a proctoring window pop-up */
|
||||
String getProctoringWindowScript(ProctoringWindowData data);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,13 +11,17 @@ package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
|
@ -29,120 +33,24 @@ public class ZoomWindowScriptResolver implements ProctoringWindowScriptResolver
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(ZoomWindowScriptResolver.class);
|
||||
|
||||
private static final String ATTR_SUBJECT = "_subject_";
|
||||
private static final String ATTR_API_KEY = "_apiKey_";
|
||||
private static final String ATTR_ACCESS_TOKEN = "_accessToken_";
|
||||
private static final String ATTR_ROOM_KEY = "_roomKey_";
|
||||
private static final String ATTR_ROOM_NAME = "_roomName_";
|
||||
private static final String ATTR_HOST = "_host_";
|
||||
private static final String ATTR_USER_NAME = "_username_";
|
||||
static final String RES_PATH =
|
||||
"classpath:ch/ethz/seb/sebserver/gui/service/session/proctoring/zoomWindow.html";
|
||||
|
||||
// @formatter:off
|
||||
private static final String ZOOM_WINDOW_HTML =
|
||||
"<html>\n"
|
||||
+ " <head>\n"
|
||||
+ " <meta charset=\"utf-8\" />\n"
|
||||
+ " <link type=\"text/css\" rel=\"stylesheet\" href=\"https://source.zoom.us/1.9.0/css/bootstrap.css\" />\n"
|
||||
+ " <link type=\"text/css\" rel=\"stylesheet\" href=\"https://source.zoom.us/1.9.0/css/react-select.css\" />\n"
|
||||
+ " </head>\n"
|
||||
+ " <body>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/react.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/react-dom.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/redux.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/redux-thunk.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/jquery.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/1.9.0/lib/vendor/lodash.min.js\"></script>\n"
|
||||
+ " <script src=\"https://source.zoom.us/zoom-meeting-1.9.0.min.js\"></script>\n"
|
||||
+ " <script src=\"https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js\"></script>\n"
|
||||
+ " <script type=\"text/javascript\">\n"
|
||||
+ "\n"
|
||||
+ " console.log(\"Checking system requirements...\");\n"
|
||||
+ " console.log(JSON.stringify(ZoomMtg.checkSystemRequirements()));\n"
|
||||
+ "\n"
|
||||
+ " console.log(\"Initializing Zoom...\");\n"
|
||||
+ " ZoomMtg.setZoomJSLib('https://source.zoom.us/1.9.0/lib', '/av');\n"
|
||||
+ " ZoomMtg.preLoadWasm();\n"
|
||||
+ " ZoomMtg.prepareJssdk();\n"
|
||||
+ "\n"
|
||||
+ " const API_KEY = \"%%_" + ATTR_API_KEY + "_%%\";\n"
|
||||
+ " const config = {\n"
|
||||
+ " meetingNumber: %%_" + ATTR_ROOM_NAME + "_%%,\n"
|
||||
+ " leaveUrl: '%%_" + ATTR_HOST + "_%%',\n"
|
||||
+ " userName: '%%_" + ATTR_USER_NAME + "_%%',\n"
|
||||
+ " passWord: '%%_" + ATTR_ROOM_KEY + "_%%',\n"
|
||||
+ " role: 1 // 1 for host; 0 for attendee\n"
|
||||
+ " };\n"
|
||||
+ "\n"
|
||||
+ " const signature = '%%_" + ATTR_ACCESS_TOKEN + "_%%';\n"
|
||||
+ "\n"
|
||||
+ " console.log(\"Initializing meeting...\");\n"
|
||||
+ "\n"
|
||||
+ " // See documentation: https://zoom.github.io/sample-app-web/ZoomMtg.html#init\n"
|
||||
+ " ZoomMtg.init({\n"
|
||||
+ " debug: true, //optional\n"
|
||||
+ " leaveUrl: config.leaveUrl, //required\n"
|
||||
+ " // webEndpoint: 'PSO web domain', // PSO option\n"
|
||||
+ " showMeetingHeader: true, //option\n"
|
||||
+ " disableInvite: false, //optional\n"
|
||||
+ " disableCallOut: false, //optional\n"
|
||||
+ " disableRecord: false, //optional\n"
|
||||
+ " disableJoinAudio: false, //optional\n"
|
||||
+ " audioPanelAlwaysOpen: true, //optional\n"
|
||||
+ " showPureSharingContent: false, //optional\n"
|
||||
+ " isSupportAV: true, //optional,\n"
|
||||
+ " isSupportChat: true, //optional,\n"
|
||||
+ " isSupportQA: true, //optional,\n"
|
||||
+ " isSupportCC: true, //optional,\n"
|
||||
+ " screenShare: true, //optional,\n"
|
||||
+ " rwcBackup: '', //optional,\n"
|
||||
+ " videoDrag: true, //optional,\n"
|
||||
+ " sharingMode: 'both', //optional,\n"
|
||||
+ " videoHeader: true, //optional,\n"
|
||||
+ " isLockBottom: true, // optional,\n"
|
||||
+ " isSupportNonverbal: true, // optional,\n"
|
||||
+ " isShowJoiningErrorDialog: true, // optional,\n"
|
||||
+ " inviteUrlFormat: '', // optional\n"
|
||||
+ " loginWindow: { // optional,\n"
|
||||
+ " width: 400,\n"
|
||||
+ " height: 380\n"
|
||||
+ " },\n"
|
||||
+ " meetingInfo: [ // optional\n"
|
||||
+ " 'topic',\n"
|
||||
+ " 'host',\n"
|
||||
+ " 'mn',\n"
|
||||
+ " 'pwd',\n"
|
||||
+ " 'invite',\n"
|
||||
+ " 'participant',\n"
|
||||
+ " 'dc'\n"
|
||||
+ " ],\n"
|
||||
+ " disableVoIP: false, // optional\n"
|
||||
+ " disableReport: false, // optional\n"
|
||||
+ " error: function (res) {\n"
|
||||
+ " console.warn(\"INIT ERROR\")\n"
|
||||
+ " console.log(res)\n"
|
||||
+ " },\n"
|
||||
+ " success: function () {\n"
|
||||
+ " console.log(\"INIT SUCCESS\")\n"
|
||||
+ " ZoomMtg.join({\n"
|
||||
+ " signature: signature,\n"
|
||||
+ " apiKey: API_KEY,\n"
|
||||
+ " meetingNumber: config.meetingNumber,\n"
|
||||
+ " userName: config.userName,\n"
|
||||
+ " passWord: config.passWord,\n"
|
||||
+ " success(res) {\n"
|
||||
+ " console.log(\"JOIN SUCCESS\")\n"
|
||||
+ " },\n"
|
||||
+ " error(res) {\n"
|
||||
+ " console.warn(\"JOIN ERROR\")\n"
|
||||
+ " console.log(res)\n"
|
||||
+ " }\n"
|
||||
+ " })\n"
|
||||
+ " }\n"
|
||||
+ " })\n"
|
||||
+ " </script>\n"
|
||||
+ " </body>\n"
|
||||
+ "</html>";
|
||||
// @formatter:on
|
||||
private static final String ATTR_SUBJECT = "ATTR_SUBJECT";
|
||||
private static final String ATTR_API_KEY = "ATTR_API_KEY";
|
||||
private static final String ATTR_ACCESS_TOKEN = "ATTR_ACCESS_TOKEN";
|
||||
private static final String ATTR_ROOM_KEY = "ATTR_ROOM_KEY";
|
||||
private static final String ATTR_ROOM_NAME = "ATTR_ROOM_NAME";
|
||||
private static final String ATTR_HOST = "ATTR_HOST";
|
||||
private static final String ATTR_USER_NAME = "ATTR_USER_NAME";
|
||||
|
||||
private final Resource resourceFile;
|
||||
|
||||
public ZoomWindowScriptResolver(
|
||||
@Value(RES_PATH) final Resource resourceFile) {
|
||||
|
||||
this.resourceFile = resourceFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(final ProctoringWindowData data) {
|
||||
|
@ -169,8 +77,22 @@ public class ZoomWindowScriptResolver implements ProctoringWindowScriptResolver
|
|||
args.put(ATTR_SUBJECT, data.connectionData.subject);
|
||||
args.put(ATTR_USER_NAME, data.connectionData.userName);
|
||||
|
||||
return new StringSubstitutor(args, "%%_", "_%%")
|
||||
.replace(ZOOM_WINDOW_HTML);
|
||||
final String htmlWindow = getHTMLWindow();
|
||||
final String replace = new StringSubstitutor(
|
||||
args,
|
||||
Constants.DYN_HTML_ATTR_OPEN,
|
||||
Constants.DYN_HTML_ATTR_CLOSE)
|
||||
.replace(htmlWindow);
|
||||
return replace;
|
||||
}
|
||||
|
||||
private String getHTMLWindow() {
|
||||
try {
|
||||
return IOUtils.toString(this.resourceFile.getInputStream());
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to load Jitsi Meet window resources", e);
|
||||
return "ERROR: " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src='https://%%_ATTR_HOST_%%/external_api.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='proctoring'></div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
const options = {
|
||||
parentNode: document.querySelector('#proctoring'),
|
||||
roomName: '%%_ATTR_ROOM_NAME_%%',
|
||||
// width: window.innerWidth,
|
||||
height: window.innerHeight - 4,
|
||||
jwt: '%%_ATTR_ACCESS_TOKEN_%%',
|
||||
configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: false, disable1On1Mode: true },
|
||||
interfaceConfigOverwrite: {
|
||||
TOOLBAR_BUTTONS: [
|
||||
'microphone', 'camera',
|
||||
'fodeviceselection', 'profile', 'chat', 'recording',
|
||||
'livestreaming', 'settings',
|
||||
'videoquality', 'filmstrip', 'feedback',
|
||||
'tileview', 'help', 'mute-everyone', 'security'
|
||||
],
|
||||
SHOW_WATERMARK_FOR_GUESTS: false,
|
||||
RECENT_LIST_ENABLED: false,
|
||||
HIDE_INVITE_MORE_HEADER: true,
|
||||
DISABLE_RINGING: true,
|
||||
DISABLE_PRESENCE_STATUS: true,
|
||||
DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
|
||||
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: false,
|
||||
MOBILE_APP_PROMO: false,
|
||||
SHOW_JITSI_WATERMARK: false,
|
||||
DISABLE_PRESENCE_STATUS: true,
|
||||
DISABLE_RINGING: true,
|
||||
DISABLE_VIDEO_BACKGROUND: false,
|
||||
filmStripOnly: false
|
||||
}
|
||||
}
|
||||
|
||||
const meetAPI = new JitsiMeetExternalAPI('%%_ATTR_HOST_%%', options);
|
||||
meetAPI.executeCommand('subject', '%%_ATTR_SUBJECT_%%');
|
||||
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,102 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<link type='text/css' rel='stylesheet' href='https://source.zoom.us/1.9.0/css/bootstrap.css' />
|
||||
<link type='text/css' rel='stylesheet' href='https://source.zoom.us/1.9.0/css/react-select.css' />
|
||||
</head>
|
||||
<body>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/react.min.js'></script>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/react-dom.min.js'></script>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/redux.min.js'></script>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/redux-thunk.min.js'></script>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/jquery.min.js'></script>
|
||||
<script src='https://source.zoom.us/1.9.0/lib/vendor/lodash.min.js'></script>
|
||||
<script src='https://source.zoom.us/zoom-meeting-1.9.0.min.js'></script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js'></script>
|
||||
<script type='text/javascript'>
|
||||
|
||||
console.log('Checking system requirements...');
|
||||
console.log(JSON.stringify(ZoomMtg.checkSystemRequirements()));
|
||||
|
||||
console.log('Initializing Zoom...');
|
||||
ZoomMtg.setZoomJSLib('https://source.zoom.us/1.9.0/lib', '/av');
|
||||
ZoomMtg.preLoadWasm();
|
||||
ZoomMtg.prepareJssdk();
|
||||
|
||||
const API_KEY = '%%_ATTR_API_KEY_%%';
|
||||
const config = {
|
||||
meetingNumber: %%_ATTR_ROOM_NAME_%%,
|
||||
leaveUrl: '%%_ATTR_HOST_%%',
|
||||
userName: '%%_ATTR_USER_NAME_%%',
|
||||
passWord: '%%_ATTR_ROOM_KEY_%%',
|
||||
role: 1 // 1 for host; 0 for attendee
|
||||
};
|
||||
|
||||
const signature = '%%_ATTR_ACCESS_TOKEN_%%';
|
||||
console.log('Initializing meeting...');
|
||||
|
||||
// See documentation: https://zoom.github.io/sample-app-web/ZoomMtg.html#init
|
||||
ZoomMtg.init({
|
||||
debug: true, //optional
|
||||
leaveUrl: config.leaveUrl, //required
|
||||
// webEndpoint: 'PSO web domain', // PSO option
|
||||
showMeetingHeader: true, //option
|
||||
disableInvite: false, //optional
|
||||
disableCallOut: false, //optional
|
||||
disableRecord: false, //optional
|
||||
disableJoinAudio: false, //optional
|
||||
audioPanelAlwaysOpen: true, //optional
|
||||
showPureSharingContent: false, //optional
|
||||
isSupportAV: true, //optional,
|
||||
isSupportChat: true, //optional,
|
||||
isSupportQA: true, //optional,
|
||||
isSupportCC: true, //optional,
|
||||
screenShare: true, //optional,
|
||||
rwcBackup: '', //optional,
|
||||
videoDrag: true, //optional,
|
||||
sharingMode: 'both', //optional,
|
||||
videoHeader: true, //optional,
|
||||
isLockBottom: true, // optional,
|
||||
isSupportNonverbal: true, // optional,
|
||||
isShowJoiningErrorDialog: true, // optional,
|
||||
inviteUrlFormat: '', // optional
|
||||
loginWindow: { // optional,
|
||||
width: 400,
|
||||
height: window.innerHeight - 4
|
||||
},
|
||||
meetingInfo: [ // optional
|
||||
'topic',
|
||||
'host',
|
||||
'mn',
|
||||
'pwd',
|
||||
'invite',
|
||||
'participant',
|
||||
'dc'
|
||||
],
|
||||
disableVoIP: false, // optional
|
||||
disableReport: false, // optional
|
||||
error: function (res) {
|
||||
console.warn('INIT ERROR')
|
||||
console.log(res)
|
||||
},
|
||||
success: function () {
|
||||
console.log('INIT SUCCESS')
|
||||
ZoomMtg.join({
|
||||
signature: signature,
|
||||
apiKey: API_KEY,
|
||||
meetingNumber: config.meetingNumber,
|
||||
userName: config.userName,
|
||||
passWord: config.passWord,
|
||||
success(res) {
|
||||
console.log('JOIN SUCCESS')
|
||||
},
|
||||
error(res) {
|
||||
console.warn('JOIN ERROR')
|
||||
console.log(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -131,7 +131,7 @@ public interface ZoomRoomRequestResponse {
|
|||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static class Settings {
|
||||
@JsonProperty final boolean host_video = false;
|
||||
@JsonProperty final boolean host_video = true;
|
||||
@JsonProperty final boolean participant_video = true;
|
||||
@JsonProperty final boolean join_before_host = true;
|
||||
@JsonProperty final int jbh_time = 0;
|
||||
|
|
|
@ -1403,7 +1403,7 @@ sebserver.examconfig.props.label.zoomFeatureFlagChat=Enable Chat
|
|||
sebserver.examconfig.props.label.zoomFeatureFlagChat.tooltip=
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagCloseCaptions=Enable Close Captions
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagCloseCaptions.tooltip=
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagDisplayingName=Display Meeting Name
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagDisplayingMeetingName=Display Meeting Name
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagDisplayMeetingName.tooltip
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagRaiseHand=Enable Raise Hand
|
||||
sebserver.examconfig.props.label.zoomFeatureFlagRaiseHand.tooltip=
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
|
||||
public class JitsiWindowScriptResolverTest {
|
||||
|
||||
@Test
|
||||
public void testJitsiWindowScriptResolver() {
|
||||
final DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
|
||||
final Resource resource = defaultResourceLoader.getResource(JitsiWindowScriptResolver.RES_PATH);
|
||||
final JitsiWindowScriptResolver jitsiWindowScriptResolver = new JitsiWindowScriptResolver(resource);
|
||||
|
||||
final ProctoringWindowData proctoringWindowData = new ProctoringWindowData(
|
||||
"0",
|
||||
"Test_Window",
|
||||
new ProctoringRoomConnection(
|
||||
ProctoringServerType.JITSI_MEET,
|
||||
"CONNECTION_TOKEN",
|
||||
"HOST",
|
||||
"URL",
|
||||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
"USER_NAME"));
|
||||
|
||||
final ProctoringWindowData proctoringWindowDataOther = new ProctoringWindowData(
|
||||
"0",
|
||||
"Test_Window",
|
||||
new ProctoringRoomConnection(
|
||||
ProctoringServerType.ZOOM,
|
||||
"CONNECTION_TOKEN",
|
||||
"HOST",
|
||||
"URL",
|
||||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
"USER_NAME"));
|
||||
|
||||
assertFalse(jitsiWindowScriptResolver.applies(proctoringWindowDataOther));
|
||||
assertTrue(jitsiWindowScriptResolver.applies(proctoringWindowData));
|
||||
|
||||
final String proctoringWindowScript = jitsiWindowScriptResolver
|
||||
.getProctoringWindowScript(proctoringWindowData);
|
||||
|
||||
assertEquals(
|
||||
"<!DOCTYPE html>\r\n"
|
||||
+ "<html>\r\n"
|
||||
+ " <head>\r\n"
|
||||
+ " <title></title>\r\n"
|
||||
+ " <script src='https://HOST/external_api.js'></script>\r\n"
|
||||
+ "</head>\r\n"
|
||||
+ "<body>\r\n"
|
||||
+ "<div id='proctoring'></div>\r\n"
|
||||
+ "</body>\r\n"
|
||||
+ "<script>\r\n"
|
||||
+ "\r\n"
|
||||
+ " const options = {\r\n"
|
||||
+ " parentNode: document.querySelector('#proctoring'),\r\n"
|
||||
+ " roomName: 'ROOM',\r\n"
|
||||
+ " // width: window.innerWidth,\r\n"
|
||||
+ " height: window.innerHeight - 4,\r\n"
|
||||
+ " jwt: 'ACCESS_TOKEN',\r\n"
|
||||
+ " configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: false, disable1On1Mode: true },\r\n"
|
||||
+ " interfaceConfigOverwrite: { \r\n"
|
||||
+ " TOOLBAR_BUTTONS: [\r\n"
|
||||
+ " 'microphone', 'camera',\r\n"
|
||||
+ " 'fodeviceselection', 'profile', 'chat', 'recording',\r\n"
|
||||
+ " 'livestreaming', 'settings',\r\n"
|
||||
+ " 'videoquality', 'filmstrip', 'feedback',\r\n"
|
||||
+ " 'tileview', 'help', 'mute-everyone', 'security'\r\n"
|
||||
+ " ],\r\n"
|
||||
+ " SHOW_WATERMARK_FOR_GUESTS: false,\r\n"
|
||||
+ " RECENT_LIST_ENABLED: false,\r\n"
|
||||
+ " HIDE_INVITE_MORE_HEADER: true,\r\n"
|
||||
+ " DISABLE_RINGING: true,\r\n"
|
||||
+ " DISABLE_PRESENCE_STATUS: true,\r\n"
|
||||
+ " DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,\r\n"
|
||||
+ " GENERATE_ROOMNAMES_ON_WELCOME_PAGE: false,\r\n"
|
||||
+ " MOBILE_APP_PROMO: false,\r\n"
|
||||
+ " SHOW_JITSI_WATERMARK: false,\r\n"
|
||||
+ " DISABLE_PRESENCE_STATUS: true,\r\n"
|
||||
+ " DISABLE_RINGING: true,\r\n"
|
||||
+ " DISABLE_VIDEO_BACKGROUND: false,\r\n"
|
||||
+ " filmStripOnly: false\r\n"
|
||||
+ " }\r\n"
|
||||
+ " }\r\n"
|
||||
+ " \r\n"
|
||||
+ " const meetAPI = new JitsiMeetExternalAPI('HOST', options);\r\n"
|
||||
+ " meetAPI.executeCommand('subject', 'SUBJECT');\r\n"
|
||||
+ " \r\n"
|
||||
+ "</script>\r\n"
|
||||
+ "</html>",
|
||||
proctoringWindowScript);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
|
||||
public class ZoomWindowScriptResolverTest {
|
||||
|
||||
@Test
|
||||
public void testJitsiWindowScriptResolver() {
|
||||
final DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
|
||||
final Resource resource = defaultResourceLoader.getResource(ZoomWindowScriptResolver.RES_PATH);
|
||||
final ZoomWindowScriptResolver zoomWindowScriptResolver = new ZoomWindowScriptResolver(resource);
|
||||
|
||||
final ProctoringWindowData proctoringWindowDataZoom = new ProctoringWindowData(
|
||||
"0",
|
||||
"Test_Window",
|
||||
new ProctoringRoomConnection(
|
||||
ProctoringServerType.ZOOM,
|
||||
"CONNECTION_TOKEN",
|
||||
"HOST",
|
||||
"URL",
|
||||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
"USER_NAME"));
|
||||
|
||||
final ProctoringWindowData proctoringWindowDataOther = new ProctoringWindowData(
|
||||
"0",
|
||||
"Test_Window",
|
||||
new ProctoringRoomConnection(
|
||||
ProctoringServerType.JITSI_MEET,
|
||||
"CONNECTION_TOKEN",
|
||||
"HOST",
|
||||
"URL",
|
||||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
"USER_NAME"));
|
||||
|
||||
assertFalse(zoomWindowScriptResolver.applies(proctoringWindowDataOther));
|
||||
assertTrue(zoomWindowScriptResolver.applies(proctoringWindowDataZoom));
|
||||
|
||||
final String proctoringWindowScript = zoomWindowScriptResolver
|
||||
.getProctoringWindowScript(proctoringWindowDataZoom);
|
||||
|
||||
assertEquals(
|
||||
"<html>\r\n"
|
||||
+ " <head>\r\n"
|
||||
+ " <meta charset='utf-8' />\r\n"
|
||||
+ " <link type='text/css' rel='stylesheet' href='https://source.zoom.us/1.9.0/css/bootstrap.css' />\r\n"
|
||||
+ " <link type='text/css' rel='stylesheet' href='https://source.zoom.us/1.9.0/css/react-select.css' />\r\n"
|
||||
+ " </head>\r\n"
|
||||
+ " <body>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/react.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/react-dom.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/redux.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/redux-thunk.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/jquery.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/1.9.0/lib/vendor/lodash.min.js'></script>\r\n"
|
||||
+ " <script src='https://source.zoom.us/zoom-meeting-1.9.0.min.js'></script>\r\n"
|
||||
+ " <script src='https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js'></script>\r\n"
|
||||
+ " <script type='text/javascript'>\r\n"
|
||||
+ "\r\n"
|
||||
+ " console.log('Checking system requirements...');\r\n"
|
||||
+ " console.log(JSON.stringify(ZoomMtg.checkSystemRequirements()));\r\n"
|
||||
+ "\r\n"
|
||||
+ " console.log('Initializing Zoom...');\r\n"
|
||||
+ " ZoomMtg.setZoomJSLib('https://source.zoom.us/1.9.0/lib', '/av');\r\n"
|
||||
+ " ZoomMtg.preLoadWasm();\r\n"
|
||||
+ " ZoomMtg.prepareJssdk();\r\n"
|
||||
+ "\r\n"
|
||||
+ " const API_KEY = 'ROOM_KEY';\r\n"
|
||||
+ " const config = {\r\n"
|
||||
+ " meetingNumber: MEETING_ID,\r\n"
|
||||
+ " leaveUrl: 'HOST',\r\n"
|
||||
+ " userName: 'USER_NAME',\r\n"
|
||||
+ " passWord: 'API_KEY',\r\n"
|
||||
+ " role: 1 // 1 for host; 0 for attendee\r\n"
|
||||
+ " };\r\n"
|
||||
+ "\r\n"
|
||||
+ " const signature = 'ACCESS_TOKEN';\r\n"
|
||||
+ " console.log('Initializing meeting...');\r\n"
|
||||
+ "\r\n"
|
||||
+ " // See documentation: https://zoom.github.io/sample-app-web/ZoomMtg.html#init\r\n"
|
||||
+ " ZoomMtg.init({\r\n"
|
||||
+ " debug: true, //optional\r\n"
|
||||
+ " leaveUrl: config.leaveUrl, //required\r\n"
|
||||
+ " // webEndpoint: 'PSO web domain', // PSO option\r\n"
|
||||
+ " showMeetingHeader: true, //option\r\n"
|
||||
+ " disableInvite: false, //optional\r\n"
|
||||
+ " disableCallOut: false, //optional\r\n"
|
||||
+ " disableRecord: false, //optional\r\n"
|
||||
+ " disableJoinAudio: false, //optional\r\n"
|
||||
+ " audioPanelAlwaysOpen: true, //optional\r\n"
|
||||
+ " showPureSharingContent: false, //optional\r\n"
|
||||
+ " isSupportAV: true, //optional,\r\n"
|
||||
+ " isSupportChat: true, //optional,\r\n"
|
||||
+ " isSupportQA: true, //optional,\r\n"
|
||||
+ " isSupportCC: true, //optional,\r\n"
|
||||
+ " screenShare: true, //optional,\r\n"
|
||||
+ " rwcBackup: '', //optional,\r\n"
|
||||
+ " videoDrag: true, //optional,\r\n"
|
||||
+ " sharingMode: 'both', //optional,\r\n"
|
||||
+ " videoHeader: true, //optional,\r\n"
|
||||
+ " isLockBottom: true, // optional,\r\n"
|
||||
+ " isSupportNonverbal: true, // optional,\r\n"
|
||||
+ " isShowJoiningErrorDialog: true, // optional,\r\n"
|
||||
+ " inviteUrlFormat: '', // optional\r\n"
|
||||
+ " loginWindow: { // optional,\r\n"
|
||||
+ " width: 400,\r\n"
|
||||
+ " height: window.innerHeight - 4\r\n"
|
||||
+ " },\r\n"
|
||||
+ " meetingInfo: [ // optional\r\n"
|
||||
+ " 'topic',\r\n"
|
||||
+ " 'host',\r\n"
|
||||
+ " 'mn',\r\n"
|
||||
+ " 'pwd',\r\n"
|
||||
+ " 'invite',\r\n"
|
||||
+ " 'participant',\r\n"
|
||||
+ " 'dc'\r\n"
|
||||
+ " ],\r\n"
|
||||
+ " disableVoIP: false, // optional\r\n"
|
||||
+ " disableReport: false, // optional\r\n"
|
||||
+ " error: function (res) {\r\n"
|
||||
+ " console.warn('INIT ERROR')\r\n"
|
||||
+ " console.log(res)\r\n"
|
||||
+ " },\r\n"
|
||||
+ " success: function () {\r\n"
|
||||
+ " console.log('INIT SUCCESS')\r\n"
|
||||
+ " ZoomMtg.join({\r\n"
|
||||
+ " signature: signature,\r\n"
|
||||
+ " apiKey: API_KEY,\r\n"
|
||||
+ " meetingNumber: config.meetingNumber,\r\n"
|
||||
+ " userName: config.userName,\r\n"
|
||||
+ " passWord: config.passWord,\r\n"
|
||||
+ " success(res) {\r\n"
|
||||
+ " console.log('JOIN SUCCESS')\r\n"
|
||||
+ " },\r\n"
|
||||
+ " error(res) {\r\n"
|
||||
+ " console.warn('JOIN ERROR')\r\n"
|
||||
+ " console.log(res)\r\n"
|
||||
+ " }\r\n"
|
||||
+ " })\r\n"
|
||||
+ " }\r\n"
|
||||
+ " })\r\n"
|
||||
+ " </script>\r\n"
|
||||
+ " </body>\r\n"
|
||||
+ "</html>",
|
||||
proctoringWindowScript);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue