diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 2ff52fbf..3c0b045e 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -249,7 +249,7 @@ namespace SafeExamBrowser.Client private IOperation BuildProctoringOperation() { - var controller = new ProctoringController(uiFactory); + var controller = new ProctoringController(ModuleLogger(nameof(ProctoringController)), uiFactory); var operation = new ProctoringOperation(context, logger, controller); context.ProctoringController = controller; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs index 23097435..0627fea0 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs @@ -17,7 +17,50 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping { switch (key) { - // TODO + case Keys.Proctoring.JitsiMeet.RoomName: + MapJitsiMeetRoomName(settings, value); + break; + case Keys.Proctoring.JitsiMeet.ServerUrl: + MapJitsiMeetServerUrl(settings, value); + break; + case Keys.Proctoring.JitsiMeet.Subject: + MapJitsiMeetSubject(settings, value); + break; + case Keys.Proctoring.JitsiMeet.Token: + MapJitsiMeetToken(settings, value); + break; + } + } + + private void MapJitsiMeetRoomName(AppSettings settings, object value) + { + if (value is string name) + { + settings.Proctoring.JitsiMeet.RoomName = name; + } + } + + private void MapJitsiMeetServerUrl(AppSettings settings, object value) + { + if (value is string url) + { + settings.Proctoring.JitsiMeet.ServerUrl = url; + } + } + + private void MapJitsiMeetSubject(AppSettings settings, object value) + { + if (value is string subject) + { + settings.Proctoring.JitsiMeet.Subject = subject; + } + } + + private void MapJitsiMeetToken(AppSettings settings, object value) + { + if (value is string token) + { + settings.Proctoring.JitsiMeet.Token = token; } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs index c9b9d4de..899f464f 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs @@ -219,6 +219,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData internal static class JitsiMeet { internal const string Enabled = "jitsiMeetEnable"; + internal const string RoomName = "jitsiMeetRoom"; + internal const string ServerUrl = "jitsiMeetServerURL"; + internal const string Subject = "jitsiMeetSubject"; + internal const string Token = "jitsiMeetToken"; } internal static class Zoom diff --git a/SafeExamBrowser.Proctoring/JitsiMeet/index.html b/SafeExamBrowser.Proctoring/JitsiMeet/index.html new file mode 100644 index 00000000..5fc8af18 --- /dev/null +++ b/SafeExamBrowser.Proctoring/JitsiMeet/index.html @@ -0,0 +1,22 @@ + +
+ + + + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Proctoring/ProctoringControl.cs b/SafeExamBrowser.Proctoring/ProctoringControl.cs index d9600a8d..bc4cfb68 100644 --- a/SafeExamBrowser.Proctoring/ProctoringControl.cs +++ b/SafeExamBrowser.Proctoring/ProctoringControl.cs @@ -6,17 +6,46 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using System; +using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.Wpf; +using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.UserInterface.Contracts.Proctoring; namespace SafeExamBrowser.Proctoring { internal class ProctoringControl : WebView2, IProctoringControl { - internal ProctoringControl() + private readonly ILogger logger; + + internal ProctoringControl(ILogger logger) { - Source = new Uri("https://www.microsoft.com"); + this.logger = logger; + CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted; + } + + private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e) + { + if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone) + { + logger.Info($"Granted access to {e.PermissionKind}."); + } + else + { + logger.Info($"Denied access to {e.PermissionKind}."); + } + } + + private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) + { + if (e.IsSuccess) + { + CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested; + logger.Info("Successfully initialized."); + } + else + { + logger.Error("Failed to initialize!", e.InitializationException); + } } } } diff --git a/SafeExamBrowser.Proctoring/ProctoringController.cs b/SafeExamBrowser.Proctoring/ProctoringController.cs index b98acf87..834e5c90 100644 --- a/SafeExamBrowser.Proctoring/ProctoringController.cs +++ b/SafeExamBrowser.Proctoring/ProctoringController.cs @@ -6,6 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; +using System.IO; +using System.Reflection; +using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Proctoring.Contracts; using SafeExamBrowser.Settings.Proctoring; using SafeExamBrowser.UserInterface.Contracts; @@ -15,31 +19,72 @@ namespace SafeExamBrowser.Proctoring { public class ProctoringController : IProctoringController { + private readonly IModuleLogger logger; private readonly IUserInterfaceFactory uiFactory; private IProctoringWindow window; - public ProctoringController(IUserInterfaceFactory uiFactory) + public ProctoringController(IModuleLogger logger, IUserInterfaceFactory uiFactory) { + this.logger = logger; this.uiFactory = uiFactory; } public void Initialize(ProctoringSettings settings) { - var control = new ProctoringControl(); + if (settings.JitsiMeet.Enabled || settings.Zoom.Enabled) + { + var control = new ProctoringControl(logger.CloneFor(nameof(ProctoringControl))); - window = uiFactory.CreateProctoringWindow(control); - window.Show(); + control.EnsureCoreWebView2Async().ContinueWith(_ => + { + control.Dispatcher.Invoke(() => control.NavigateToString(LoadContent(settings))); + }); - // TODO - //var content = load Zoom page, replace //INDEX_JS//; + window = uiFactory.CreateProctoringWindow(control); + window.Show(); - //control.NavigateToString(content); + logger.Info($"Initialized proctoring with {(settings.JitsiMeet.Enabled ? "Jitsi Meet" : "Zoom")}."); + } + else + { + logger.Warn("Failed to initialize remote proctoring because no provider is enabled in the active configuration."); + } } public void Terminate() { window?.Close(); } + + private string LoadContent(ProctoringSettings settings) + { + var provider = settings.JitsiMeet.Enabled ? "JitsiMeet" : "Zoom"; + var assembly = Assembly.GetAssembly(typeof(ProctoringController)); + var path = $"{typeof(ProctoringController).Namespace}.{provider}.index.html"; + + using (var stream = assembly.GetManifestResourceStream(path)) + using (var reader = new StreamReader(stream)) + { + var html = reader.ReadToEnd(); + + if (settings.JitsiMeet.Enabled) + { + html = html.Replace("%%_DOMAIN_%%", settings.JitsiMeet.ServerUrl); + html = html.Replace("%%_ROOM_NAME_%%", settings.JitsiMeet.RoomName); + html = html.Replace("%%_SUBJECT_%%", settings.JitsiMeet.Subject); + html = html.Replace("%%_TOKEN_%%", settings.JitsiMeet.Token); + } + else if (settings.Zoom.Enabled) + { + html = html.Replace("%%_API_KEY_%%", settings.Zoom.ApiKey); + html = html.Replace("%%_API_SECRET_%%", settings.Zoom.ApiSecret); + html = html.Replace("123456789", Convert.ToString(settings.Zoom.MeetingNumber)); + html = html.Replace("%%_USER_NAME_%%", settings.Zoom.UserName); + } + + return html; + } + } } } diff --git a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj index 1c1bf007..c81eeb22 100644 --- a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj +++ b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj @@ -95,12 +95,10 @@