SEBWIN-475: Implemented basic Zoom proctoring integration.
This commit is contained in:
parent
7ad1d6ae5d
commit
da39fb1f59
8 changed files with 120 additions and 32 deletions
|
@ -92,6 +92,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.Proctoring.Zoom.Signature:
|
||||
MapZoomSignature(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.Subject:
|
||||
MapZoomSubject(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.UserName:
|
||||
MapZoomUserName(settings, value);
|
||||
break;
|
||||
|
@ -317,6 +320,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
}
|
||||
}
|
||||
|
||||
private void MapZoomSubject(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string subject)
|
||||
{
|
||||
settings.Proctoring.Zoom.Subject = subject;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomUserName(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string name)
|
||||
|
|
|
@ -233,7 +233,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Proctoring.JitsiMeet.VideoMuted = false;
|
||||
settings.Proctoring.ShowTaskbarNotification = true;
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
|
||||
settings.Proctoring.Zoom.AllowChat = false;
|
||||
settings.Proctoring.Zoom.Enabled = false;
|
||||
settings.Proctoring.Zoom.ReceiveAudio = false;
|
||||
settings.Proctoring.Zoom.ReceiveVideo = false;
|
||||
|
||||
settings.Security.AllowApplicationLogAccess = false;
|
||||
settings.Security.AllowTermination = true;
|
||||
|
|
|
@ -255,6 +255,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string Enabled = "zoomEnable";
|
||||
internal const string MeetingNumber = "zoomRoom";
|
||||
internal const string Signature = "zoomToken";
|
||||
internal const string Subject = "zoomSubject";
|
||||
internal const string UserName = "zoomUserInfoDisplayName";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
settings.Zoom.MeetingNumber = args.ZoomMeetingNumber;
|
||||
settings.Zoom.Password = args.ZoomPassword;
|
||||
settings.Zoom.Signature = args.ZoomSignature;
|
||||
settings.Zoom.Subject = args.ZoomSubject;
|
||||
settings.Zoom.UserName = args.ZoomUserName;
|
||||
|
||||
StopProctoring();
|
||||
|
@ -140,6 +141,10 @@ namespace SafeExamBrowser.Proctoring
|
|||
settings.JitsiMeet.ReceiveAudio = receiveAudio;
|
||||
settings.JitsiMeet.ReceiveVideo = receiveVideo;
|
||||
|
||||
settings.Zoom.AllowChat = allowChat;
|
||||
settings.Zoom.ReceiveAudio = receiveAudio;
|
||||
settings.Zoom.ReceiveVideo = receiveVideo;
|
||||
|
||||
if (allowChat || receiveVideo)
|
||||
{
|
||||
settings.WindowVisibility = WindowVisibility.AllowToHide;
|
||||
|
@ -149,7 +154,6 @@ namespace SafeExamBrowser.Proctoring
|
|||
settings.WindowVisibility = windowVisibility;
|
||||
}
|
||||
|
||||
// TODO: This is apparently not necessary for Zoom, there we can enable / disable the options via API call!
|
||||
StopProctoring();
|
||||
StartProctoring();
|
||||
}
|
||||
|
@ -176,7 +180,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
});
|
||||
|
||||
window = uiFactory.CreateProctoringWindow(control);
|
||||
window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : settings.Zoom.UserName);
|
||||
window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : settings.Zoom.Subject);
|
||||
window.Show();
|
||||
|
||||
if (settings.WindowVisibility == WindowVisibility.AllowToShow || settings.WindowVisibility == WindowVisibility.Hidden)
|
||||
|
@ -203,7 +207,15 @@ namespace SafeExamBrowser.Proctoring
|
|||
{
|
||||
control.Dispatcher.Invoke(() =>
|
||||
{
|
||||
control.ExecuteScriptAsync("api.executeCommand('hangup'); api.dispose();");
|
||||
if (settings.JitsiMeet.Enabled)
|
||||
{
|
||||
control.ExecuteScriptAsync("api.executeCommand('hangup'); api.dispose();");
|
||||
}
|
||||
else if (settings.Zoom.Enabled)
|
||||
{
|
||||
control.ExecuteScriptAsync("ZoomMtg.leaveMeeting({});");
|
||||
}
|
||||
|
||||
window.Close();
|
||||
control = default(ProctoringControl);
|
||||
window = default(IProctoringWindow);
|
||||
|
@ -242,6 +254,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
}
|
||||
else if (settings.Zoom.Enabled)
|
||||
{
|
||||
html = html.Replace("'%_ALLOW_CHAT_%'", settings.Zoom.AllowChat ? "true" : "false");
|
||||
html = html.Replace("%%_API_KEY_%%", settings.Zoom.ApiKey);
|
||||
html = html.Replace("%%_API_SECRET_%%", settings.Zoom.ApiSecret);
|
||||
html = html.Replace("%%_MEETING_NUMBER_%%", settings.Zoom.MeetingNumber);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
const API_SECRET = '%%_API_SECRET_%%';
|
||||
const ATTENDEE = 0;
|
||||
|
||||
var audioJoin = 0;
|
||||
var configuration = {
|
||||
leaveUrl: 'doesnotexist',
|
||||
meetingNumber: '%%_MEETING_NUMBER_%%',
|
||||
|
@ -26,6 +27,39 @@
|
|||
userName: '%%_USER_NAME_%%'
|
||||
};
|
||||
var signature = '%%_SIGNATURE_%%';
|
||||
var videoJoin = 0;
|
||||
|
||||
function initializeUserInterface(_) {
|
||||
var audioButton = document.getElementsByClassName('join-audio-by-voip__join-btn')[0];
|
||||
var audioContainer = document.getElementsByClassName('join-audio-container')[0];
|
||||
var leave = document.getElementsByClassName('footer__leave-btn-container')[0];
|
||||
var videoButton = document.getElementsByClassName('send-video-container__btn')[0];
|
||||
var videoContainer = document.getElementsByClassName('send-video-container')[0];
|
||||
|
||||
if (audioButton && audioJoin < 100) {
|
||||
audioButton.click();
|
||||
audioJoin++;
|
||||
}
|
||||
|
||||
if (audioContainer) {
|
||||
audioContainer.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
if (leave) {
|
||||
leave.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
if (videoButton && videoJoin < 100) {
|
||||
videoButton.click();
|
||||
videoJoin++;
|
||||
}
|
||||
|
||||
if (videoContainer) {
|
||||
videoContainer.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
requestAnimationFrame(initializeUserInterface);
|
||||
};
|
||||
|
||||
if (!ZoomMtg.checkSystemRequirements()) {
|
||||
alert('This system does not meet the necessary requirements for Zoom!');
|
||||
|
@ -42,46 +76,47 @@
|
|||
apiSecret: API_SECRET,
|
||||
role: configuration.role,
|
||||
error: function (res) {
|
||||
alert(`Failed to generate signature: ${JSON.stringify(res)}`)
|
||||
alert(`Failed to generate signature: ${JSON.stringify(res)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ZoomMtg.init({
|
||||
leaveUrl: configuration.leaveUrl,
|
||||
showMeetingHeader: true,
|
||||
disableInvite: false,
|
||||
disableCallOut: false,
|
||||
disableRecord: false,
|
||||
audioPanelAlwaysOpen: false,
|
||||
disableCallOut: true,
|
||||
disableInvite: true,
|
||||
disableJoinAudio: false,
|
||||
audioPanelAlwaysOpen: true,
|
||||
showPureSharingContent: false,
|
||||
isSupportAV: true,
|
||||
isSupportChat: true,
|
||||
isSupportQA: true,
|
||||
isSupportCC: true,
|
||||
screenShare: true,
|
||||
videoDrag: true,
|
||||
sharingMode: 'both',
|
||||
videoHeader: true,
|
||||
disableRecord: true,
|
||||
disableReport: true,
|
||||
disableVoIP: false,
|
||||
leaveUrl: configuration.leaveUrl,
|
||||
isLockBottom: true,
|
||||
isSupportNonverbal: true,
|
||||
isShowJoiningErrorDialog: true,
|
||||
inviteUrlFormat: '',
|
||||
isSupportAV: true,
|
||||
isSupportBreakout: false,
|
||||
isSupportChat: '%_ALLOW_CHAT_%',
|
||||
isSupportCC: false,
|
||||
isSupportPolling: false,
|
||||
isSupportQA: false,
|
||||
isSupportNonverbal: false,
|
||||
screenShare: false,
|
||||
sharingMode: 'both',
|
||||
showMeetingHeader: true,
|
||||
showPureSharingContent: false,
|
||||
videoDrag: true,
|
||||
videoHeader: true,
|
||||
meetingInfo: [
|
||||
'topic',
|
||||
'host',
|
||||
'mn',
|
||||
'pwd',
|
||||
'telPwd',
|
||||
'invite',
|
||||
'participant',
|
||||
'dc'
|
||||
//'mn',
|
||||
//'pwd',
|
||||
//'telPwd',
|
||||
//'invite',
|
||||
//'dc'
|
||||
],
|
||||
disableVoIP: false,
|
||||
disableReport: false,
|
||||
error: function (res) {
|
||||
alert(`Failed to initialize meeting: ${JSON.stringify(res)}`)
|
||||
alert(`Failed to initialize meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function () {
|
||||
ZoomMtg.join({
|
||||
|
@ -91,11 +126,14 @@
|
|||
signature: signature,
|
||||
userName: configuration.userName,
|
||||
error: function (res) {
|
||||
alert(`Failed to join meeting: ${JSON.stringify(res)}`)
|
||||
alert(`Failed to join meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function (res) {
|
||||
requestAnimationFrame(initializeUserInterface);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -20,6 +20,7 @@ namespace SafeExamBrowser.Server.Contracts.Events
|
|||
public string ZoomMeetingNumber { get; set; }
|
||||
public string ZoomPassword { get; set; }
|
||||
public string ZoomSignature { get; set; }
|
||||
public string ZoomSubject { get; set; }
|
||||
public string ZoomUserName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,6 +206,7 @@ namespace SafeExamBrowser.Server
|
|||
attributes.Instruction.ZoomMeetingNumber = attributesJson["zoomRoom"].Value<string>();
|
||||
attributes.Instruction.ZoomPassword = attributesJson["zoomMeetingKey"].Value<string>();
|
||||
attributes.Instruction.ZoomSignature = attributesJson["zoomToken"].Value<string>();
|
||||
attributes.Instruction.ZoomSubject = attributesJson["zoomSubject"].Value<string>();
|
||||
attributes.Instruction.ZoomUserName = attributesJson["zoomUserName"].Value<string>();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,11 @@ namespace SafeExamBrowser.Settings.Proctoring
|
|||
[Serializable]
|
||||
public class ZoomSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the user can use the chat.
|
||||
/// </summary>
|
||||
public bool AllowChat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The API key to be used for authentication.
|
||||
/// </summary>
|
||||
|
@ -41,11 +46,26 @@ namespace SafeExamBrowser.Settings.Proctoring
|
|||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user may receive the video stream of other meeting participants.
|
||||
/// </summary>
|
||||
public bool ReceiveAudio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user may receive the audio stream of other meeting participants.
|
||||
/// </summary>
|
||||
public bool ReceiveVideo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The signature to be used for authentication.
|
||||
/// </summary>
|
||||
public string Signature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The subject of the meeting.
|
||||
/// </summary>
|
||||
public string Subject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user name to be used for the meeting.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue