From 345c5b7b141c893cb9b2a2e0dc4a9866b8be080c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Fri, 19 Feb 2021 22:03:52 +0100 Subject: [PATCH] SEBWIN-449: Added WebView2 with scaffolding for remote proctoring UI. --- SafeExamBrowser.Client/CompositionRoot.cs | 2 +- .../Operations/ProctoringOperation.cs | 22 ++++- SafeExamBrowser.I18n.Contracts/TextKey.cs | 4 + .../Applications/ApplicationMonitor.cs | 3 +- .../IProctoringController.cs | 10 +++ ...afeExamBrowser.Proctoring.Contracts.csproj | 6 ++ .../ProctoringControl.cs | 22 +++++ .../ProctoringController.cs | 23 ++++++ .../SafeExamBrowser.Proctoring.csproj | 34 ++++++++ SafeExamBrowser.Proctoring/packages.config | 4 + .../IUserInterfaceFactory.cs | 6 ++ .../Proctoring/IProctoringControl.cs | 18 +++++ .../Proctoring/IProctoringWindow.cs | 20 +++++ ...ExamBrowser.UserInterface.Contracts.csproj | 2 + ...feExamBrowser.UserInterface.Desktop.csproj | 7 ++ .../UserInterfaceFactory.cs | 6 ++ .../Windows/ProctoringWindow.xaml | 10 +++ .../Windows/ProctoringWindow.xaml.cs | 80 +++++++++++++++++++ .../UserInterfaceFactory.cs | 7 ++ 19 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 SafeExamBrowser.Proctoring/ProctoringControl.cs create mode 100644 SafeExamBrowser.Proctoring/packages.config create mode 100644 SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs create mode 100644 SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs create mode 100644 SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml create mode 100644 SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 2a2fb805..2ff52fbf 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(); + var controller = new ProctoringController(uiFactory); var operation = new ProctoringOperation(context, logger, controller); context.ProctoringController = controller; diff --git a/SafeExamBrowser.Client/Operations/ProctoringOperation.cs b/SafeExamBrowser.Client/Operations/ProctoringOperation.cs index 22077917..802cac39 100644 --- a/SafeExamBrowser.Client/Operations/ProctoringOperation.cs +++ b/SafeExamBrowser.Client/Operations/ProctoringOperation.cs @@ -8,6 +8,7 @@ using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; +using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Proctoring.Contracts; @@ -18,7 +19,7 @@ namespace SafeExamBrowser.Client.Operations private readonly ILogger logger; private readonly IProctoringController controller; - public override event ActionRequiredEventHandler ActionRequired; + public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event StatusChangedEventHandler StatusChanged; public ProctoringOperation(ClientContext context, ILogger logger, IProctoringController controller) : base(context) @@ -29,11 +30,30 @@ namespace SafeExamBrowser.Client.Operations public override OperationResult Perform() { + // TODO + Context.Settings.Proctoring.Enabled = true; + + if (Context.Settings.Proctoring.Enabled) + { + logger.Info("Initializing proctoring..."); + StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProctoring); + + controller.Initialize(Context.Settings.Proctoring); + } + return OperationResult.Success; } public override OperationResult Revert() { + if (Context.Settings.Proctoring.Enabled) + { + logger.Info("Terminating proctoring..."); + StatusChanged?.Invoke(TextKey.OperationStatus_TerminateProctoring); + + controller.Terminate(); + } + return OperationResult.Success; } } diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index dd53c550..45a20622 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -139,6 +139,8 @@ namespace SafeExamBrowser.I18n.Contracts OperationStatus_InitializeBrowser, OperationStatus_InitializeConfiguration, OperationStatus_InitializeKioskMode, + // TODO + OperationStatus_InitializeProctoring, OperationStatus_InitializeRuntimeConnection, OperationStatus_InitializeServer, OperationStatus_InitializeServiceSession, @@ -158,6 +160,8 @@ namespace SafeExamBrowser.I18n.Contracts OperationStatus_StopKeyboardInterception, OperationStatus_StopMouseInterception, OperationStatus_TerminateBrowser, + // TODO + OperationStatus_TerminateProctoring, OperationStatus_TerminateShell, OperationStatus_ValidateRemoteSessionPolicy, OperationStatus_ValidateVirtualMachinePolicy, diff --git a/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs b/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs index c6177ad4..f1a326f6 100644 --- a/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs +++ b/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs @@ -227,8 +227,9 @@ namespace SafeExamBrowser.Monitoring.Applications { var isRuntime = process.Name == "SafeExamBrowser.exe" && process.OriginalName == "SafeExamBrowser.exe"; var isClient = process.Name == "SafeExamBrowser.Client.exe" && process.OriginalName == "SafeExamBrowser.Client.exe"; + var isWebView = process.Name == "msedgewebview2.exe" && process.OriginalName == "msedgewebview2.exe"; - return isRuntime || isClient; + return isRuntime || isClient || isWebView; } private void Close(Window window) diff --git a/SafeExamBrowser.Proctoring.Contracts/IProctoringController.cs b/SafeExamBrowser.Proctoring.Contracts/IProctoringController.cs index ca771634..d2443764 100644 --- a/SafeExamBrowser.Proctoring.Contracts/IProctoringController.cs +++ b/SafeExamBrowser.Proctoring.Contracts/IProctoringController.cs @@ -6,6 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using SafeExamBrowser.Settings.Proctoring; + namespace SafeExamBrowser.Proctoring.Contracts { /// @@ -13,6 +15,14 @@ namespace SafeExamBrowser.Proctoring.Contracts /// public interface IProctoringController { + /// + /// + /// + void Initialize(ProctoringSettings settings); + /// + /// + /// + void Terminate(); } } diff --git a/SafeExamBrowser.Proctoring.Contracts/SafeExamBrowser.Proctoring.Contracts.csproj b/SafeExamBrowser.Proctoring.Contracts/SafeExamBrowser.Proctoring.Contracts.csproj index f4f0ef87..0df5942c 100644 --- a/SafeExamBrowser.Proctoring.Contracts/SafeExamBrowser.Proctoring.Contracts.csproj +++ b/SafeExamBrowser.Proctoring.Contracts/SafeExamBrowser.Proctoring.Contracts.csproj @@ -57,5 +57,11 @@ + + + {30b2d907-5861-4f39-abad-c4abf1b3470e} + SafeExamBrowser.Settings + + \ No newline at end of file diff --git a/SafeExamBrowser.Proctoring/ProctoringControl.cs b/SafeExamBrowser.Proctoring/ProctoringControl.cs new file mode 100644 index 00000000..d9600a8d --- /dev/null +++ b/SafeExamBrowser.Proctoring/ProctoringControl.cs @@ -0,0 +1,22 @@ +/* + * 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/. + */ + +using System; +using Microsoft.Web.WebView2.Wpf; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; + +namespace SafeExamBrowser.Proctoring +{ + internal class ProctoringControl : WebView2, IProctoringControl + { + internal ProctoringControl() + { + Source = new Uri("https://www.microsoft.com"); + } + } +} diff --git a/SafeExamBrowser.Proctoring/ProctoringController.cs b/SafeExamBrowser.Proctoring/ProctoringController.cs index f5213743..c944e5b5 100644 --- a/SafeExamBrowser.Proctoring/ProctoringController.cs +++ b/SafeExamBrowser.Proctoring/ProctoringController.cs @@ -7,11 +7,34 @@ */ using SafeExamBrowser.Proctoring.Contracts; +using SafeExamBrowser.Settings.Proctoring; +using SafeExamBrowser.UserInterface.Contracts; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; namespace SafeExamBrowser.Proctoring { public class ProctoringController : IProctoringController { + private readonly IUserInterfaceFactory uiFactory; + private IProctoringWindow window; + + public ProctoringController(IUserInterfaceFactory uiFactory) + { + this.uiFactory = uiFactory; + } + + public void Initialize(ProctoringSettings settings) + { + var control = new ProctoringControl(); + + window = uiFactory.CreateProctoringWindow(control); + window.Show(); + } + + public void Terminate() + { + window?.Close(); + } } } diff --git a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj index f1c5af53..3d4cf20a 100644 --- a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj +++ b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj @@ -12,6 +12,8 @@ v4.7.2 512 true + + true @@ -50,10 +52,24 @@ MinimumRecommendedRules.ruleset + + ..\packages\Microsoft.Web.WebView2.1.0.705.50\lib\net45\Microsoft.Web.WebView2.Core.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.705.50\lib\net45\Microsoft.Web.WebView2.WinForms.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.705.50\lib\net45\Microsoft.Web.WebView2.Wpf.dll + + + + + + @@ -66,6 +82,24 @@ {8e52bd1c-0540-4f16-b181-6665d43f7a7b} SafeExamBrowser.Proctoring.Contracts + + {30b2d907-5861-4f39-abad-c4abf1b3470e} + SafeExamBrowser.Settings + + + {c7889e97-6ff6-4a58-b7cb-521ed276b316} + SafeExamBrowser.UserInterface.Contracts + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/SafeExamBrowser.Proctoring/packages.config b/SafeExamBrowser.Proctoring/packages.config new file mode 100644 index 00000000..84941d6b --- /dev/null +++ b/SafeExamBrowser.Proctoring/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs index e90f2ffe..0d7169b6 100644 --- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs @@ -19,6 +19,7 @@ using SafeExamBrowser.SystemComponents.Contracts.Keyboard; using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork; using SafeExamBrowser.UserInterface.Contracts.Browser; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Windows; using SafeExamBrowser.UserInterface.Contracts.Windows.Data; @@ -95,6 +96,11 @@ namespace SafeExamBrowser.UserInterface.Contracts /// ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location); + /// + /// Creates a new proctoring window loaded with the given proctoring control. + /// + IProctoringWindow CreateProctoringWindow(IProctoringControl control); + /// /// Creates a new runtime window which runs on its own thread. /// diff --git a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs new file mode 100644 index 00000000..c6717b54 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs @@ -0,0 +1,18 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.UserInterface.Contracts.Proctoring +{ + /// + /// + /// + public interface IProctoringControl + { + + } +} diff --git a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs new file mode 100644 index 00000000..d90e8dee --- /dev/null +++ b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs @@ -0,0 +1,20 @@ +/* + * 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/. + */ + +using SafeExamBrowser.UserInterface.Contracts.Windows; + +namespace SafeExamBrowser.UserInterface.Contracts.Proctoring +{ + /// + /// + /// + public interface IProctoringWindow : IWindow + { + + } +} diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj index d790bdc3..4ab5b7fb 100644 --- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj +++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj @@ -67,6 +67,8 @@ + + diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index a99e82d2..50197dc6 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -165,6 +165,9 @@ PasswordDialog.xaml + + ProctoringWindow.xaml + RuntimeWindow.xaml @@ -350,6 +353,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index fadc4b49..1b148e27 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -24,6 +24,7 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Browser; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Windows; using SafeExamBrowser.UserInterface.Contracts.Windows.Data; @@ -162,6 +163,11 @@ namespace SafeExamBrowser.UserInterface.Desktop } } + public IProctoringWindow CreateProctoringWindow(IProctoringControl control) + { + return Application.Current.Dispatcher.Invoke(() => new ProctoringWindow(control)); + } + public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig) { return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text)); diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml new file mode 100644 index 00000000..d384d444 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml @@ -0,0 +1,10 @@ + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs new file mode 100644 index 00000000..29504455 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs @@ -0,0 +1,80 @@ +/* + * 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/. + */ + +using System.ComponentModel; +using System.Windows; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; +using SafeExamBrowser.UserInterface.Contracts.Windows; +using SafeExamBrowser.UserInterface.Contracts.Windows.Events; + +namespace SafeExamBrowser.UserInterface.Desktop.Windows +{ + public partial class ProctoringWindow : Window, IProctoringWindow + { + private WindowClosingEventHandler closing; + + event WindowClosingEventHandler IWindow.Closing + { + add { closing += value; } + remove { closing -= value; } + } + + public ProctoringWindow(IProctoringControl control) + { + InitializeComponent(); + InitializeWindow(control); + } + + public void BringToForeground() + { + Dispatcher.Invoke(() => + { + if (WindowState == WindowState.Minimized) + { + WindowState = WindowState.Normal; + } + + Activate(); + }); + } + + public new void Close() + { + Dispatcher.Invoke(() => + { + closing?.Invoke(); + base.Close(); + }); + } + + public new void Hide() + { + Dispatcher.Invoke(base.Hide); + } + + public new void Show() + { + Dispatcher.Invoke(base.Show); + } + + private void ProctoringWindow_Closing(object sender, CancelEventArgs e) + { + closing?.Invoke(); + } + + private void InitializeWindow(object control) + { + if (control is UIElement element) + { + ControlContainer.Children.Add(element); + } + + Closing += ProctoringWindow_Closing; + } + } +} diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs index e22c42de..c4d94527 100644 --- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs @@ -24,6 +24,7 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Browser; +using SafeExamBrowser.UserInterface.Contracts.Proctoring; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Windows; using SafeExamBrowser.UserInterface.Contracts.Windows.Data; @@ -162,6 +163,12 @@ namespace SafeExamBrowser.UserInterface.Mobile } } + public IProctoringWindow CreateProctoringWindow(IProctoringControl control) + { + // TODO + throw new System.NotImplementedException(); + } + public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig) { return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));