From bc06a0c985d8e215f7a4a0e9516459690db0a8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Tue, 28 Jul 2020 19:56:25 +0200 Subject: [PATCH] SEBWIN-405: Prepared infrastructure in client for server functionality. --- SafeExamBrowser.Client/ClientContext.cs | 6 ++ SafeExamBrowser.Client/ClientController.cs | 12 +++ SafeExamBrowser.Client/CompositionRoot.cs | 10 +++ .../Operations/ServerOperation.cs | 75 +++++++++++++++++++ .../SafeExamBrowser.Client.csproj | 9 +++ .../AppConfig.cs | 15 ++++ .../Events/ExamSelectionEventArgs.cs | 2 +- .../Operations/ServerOperation.cs | 13 ++-- .../Data/ConnectionInfo.cs | 31 ++++++++ .../{ => Data}/Exam.cs | 2 +- .../{ => Data}/ServerResponse.cs | 2 +- .../IServerProxy.cs | 24 ++++-- .../SafeExamBrowser.Server.Contracts.csproj | 5 +- SafeExamBrowser.Server/Data/ApiVersion1.cs | 4 - SafeExamBrowser.Server/ServerProxy.cs | 26 ++++++- .../IUserInterfaceFactory.cs | 2 +- .../Windows/Data/ExamSelectionDialogResult.cs | 2 +- .../UserInterfaceFactory.cs | 2 +- .../Windows/ExamSelectionDialog.xaml.cs | 2 +- .../UserInterfaceFactory.cs | 2 +- 20 files changed, 216 insertions(+), 30 deletions(-) create mode 100644 SafeExamBrowser.Client/Operations/ServerOperation.cs create mode 100644 SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs rename SafeExamBrowser.Server.Contracts/{ => Data}/Exam.cs (94%) rename SafeExamBrowser.Server.Contracts/{ => Data}/ServerResponse.cs (96%) diff --git a/SafeExamBrowser.Client/ClientContext.cs b/SafeExamBrowser.Client/ClientContext.cs index 422eb0f3..901bf1e7 100644 --- a/SafeExamBrowser.Client/ClientContext.cs +++ b/SafeExamBrowser.Client/ClientContext.cs @@ -12,6 +12,7 @@ using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Configuration.Contracts; +using SafeExamBrowser.Server.Contracts; using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -47,6 +48,11 @@ namespace SafeExamBrowser.Client /// internal IClientHost ClientHost { get; set; } + /// + /// The server proxy, if the current session mode is . + /// + internal IServerProxy Server { get; set; } + /// /// The identifier of the current session. /// diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index ddcb55e0..86ca9047 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -26,6 +26,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Display; using SafeExamBrowser.Monitoring.Contracts.System; +using SafeExamBrowser.Server.Contracts; using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog; @@ -60,6 +61,7 @@ namespace SafeExamBrowser.Client private IBrowserApplication Browser => context.Browser; private IClientHost ClientHost => context.ClientHost; + private IServerProxy Server => context.Server; private AppSettings Settings => context.Settings; internal ClientController( @@ -196,6 +198,11 @@ namespace SafeExamBrowser.Client { activator.Activated += TerminationActivator_Activated; } + + if (Server != null) + { + // TODO + } } private void DeregisterEvents() @@ -222,6 +229,11 @@ namespace SafeExamBrowser.Client ClientHost.Shutdown -= ClientHost_Shutdown; } + if (Server != null) + { + // TODO + } + foreach (var activator in context.Activators.OfType()) { activator.Activated -= TerminationActivator_Activated; diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 3a885aee..34936310 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -31,6 +31,7 @@ using SafeExamBrowser.Monitoring.Display; using SafeExamBrowser.Monitoring.Keyboard; using SafeExamBrowser.Monitoring.Mouse; using SafeExamBrowser.Monitoring.System; +using SafeExamBrowser.Server; using SafeExamBrowser.Settings.Logging; using SafeExamBrowser.Settings.UserInterface; using SafeExamBrowser.SystemComponents; @@ -118,6 +119,7 @@ namespace SafeExamBrowser.Client operations.Enqueue(new SystemMonitorOperation(context, systemMonitor, logger)); operations.Enqueue(new LazyInitializationOperation(BuildShellOperation)); operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); + operations.Enqueue(new LazyInitializationOperation(BuildServerOperation)); operations.Enqueue(new ClipboardOperation(context, logger, nativeMethods)); var sequence = new OperationSequence(logger, operations); @@ -237,6 +239,14 @@ namespace SafeExamBrowser.Client return operation; } + private IOperation BuildServerOperation() + { + var server = new ServerProxy(context.AppConfig, logger); + var operation = new ServerOperation(actionCenter, context, logger, server, taskbar); + + return operation; + } + private IOperation BuildShellOperation() { var aboutInfo = new AboutNotificationInfo(text); diff --git a/SafeExamBrowser.Client/Operations/ServerOperation.cs b/SafeExamBrowser.Client/Operations/ServerOperation.cs new file mode 100644 index 00000000..189930d3 --- /dev/null +++ b/SafeExamBrowser.Client/Operations/ServerOperation.cs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 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.Core.Contracts.OperationModel; +using SafeExamBrowser.Core.Contracts.OperationModel.Events; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Settings; +using SafeExamBrowser.UserInterface.Contracts.Shell; + +namespace SafeExamBrowser.Client.Operations +{ + internal class ServerOperation : ClientOperation + { + private readonly IActionCenter actionCenter; + private readonly ILogger logger; + private readonly IServerProxy server; + private readonly ITaskbar taskbar; + + public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } + public override event StatusChangedEventHandler StatusChanged; + + public ServerOperation( + IActionCenter actionCenter, + ClientContext context, + ILogger logger, + IServerProxy server, + ITaskbar taskbar) : base(context) + { + this.actionCenter = actionCenter; + this.logger = logger; + this.server = server; + this.taskbar = taskbar; + } + + public override OperationResult Perform() + { + var result = OperationResult.Success; + + if (Context.Settings.SessionMode == SessionMode.Server) + { + logger.Info("Initializing server..."); + StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer); + + server.Initialize(Context.AppConfig.ServerApi, Context.AppConfig.ServerConnectionToken, Context.AppConfig.ServerOauth2Token, Context.Settings.Server); + + // TODO: Add action center and taskbar notifications + } + + return result; + } + + public override OperationResult Revert() + { + var result = OperationResult.Success; + + if (Context.Settings?.SessionMode == SessionMode.Server) + { + logger.Info("Finalizing server..."); + StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer); + + // TODO: Stop sending pings and logs (or in controller?) + // TODO: Stop action center and taskbar notifications + } + + return result; + } + } +} diff --git a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj index 3d63f785..00a9545e 100644 --- a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj +++ b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj @@ -94,6 +94,7 @@ + @@ -193,6 +194,14 @@ {EF563531-4EB5-44B9-A5EC-D6D6F204469B} SafeExamBrowser.Monitoring + + {db701e6f-bddc-4cec-b662-335a9dc11809} + SafeExamBrowser.Server.Contracts + + + {46edbde0-58b4-4725-9783-0c55c3d49c0c} + SafeExamBrowser.Server + {30b2d907-5861-4f39-abad-c4abf1b3470e} SafeExamBrowser.Settings diff --git a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs index f7d68a57..245f4608 100644 --- a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs +++ b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs @@ -151,6 +151,21 @@ namespace SafeExamBrowser.Configuration.Contracts /// public string SebUriSchemeSecure { get; set; } + /// + /// The server API as JSON string. + /// + public string ServerApi { get; set; } + + /// + /// The connection token for a server. + /// + public string ServerConnectionToken { get; set; } + + /// + /// The OAuth2 token for a server. + /// + public string ServerOauth2Token { get; set; } + /// /// The communication address of the service component. /// diff --git a/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs b/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs index d469ec0b..9089ad6c 100644 --- a/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs +++ b/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using SafeExamBrowser.Core.Contracts.OperationModel.Events; -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; namespace SafeExamBrowser.Runtime.Operations.Events { diff --git a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs index 68eb0aed..cdac4bda 100644 --- a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs @@ -15,6 +15,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Operations.Events; using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings; using SafeExamBrowser.SystemComponents.Contracts; @@ -53,7 +54,7 @@ namespace SafeExamBrowser.Runtime.Operations server.Initialize(Context.Next.Settings.Server); - var (abort, fallback, success) = TryPerformWithFallback(() => server.Connect(), out var token); + var (abort, fallback, success) = TryPerformWithFallback(() => server.Connect()); if (success) { @@ -69,12 +70,16 @@ namespace SafeExamBrowser.Runtime.Operations if (success) { + var info = server.GetConnectionInfo(); var status = TryLoadSettings(uri, UriSource.Server, out _, out var settings); fileSystem.Delete(uri.LocalPath); if (status == LoadStatus.Success) { + Context.Next.AppConfig.ServerApi = info.Api; + Context.Next.AppConfig.ServerConnectionToken = info.ConnectionToken; + Context.Next.AppConfig.ServerOauth2Token = info.Oauth2Token; Context.Next.Settings = settings; Context.Next.Settings.Browser.StartUrl = exam.Url; result = OperationResult.Success; @@ -121,7 +126,7 @@ namespace SafeExamBrowser.Runtime.Operations public override OperationResult Revert() { - var result = OperationResult.Failed; + var result = OperationResult.Success; if (Context.Current?.Settings.SessionMode == SessionMode.Server) { @@ -139,10 +144,6 @@ namespace SafeExamBrowser.Runtime.Operations result = OperationResult.Failed; } } - else - { - result = OperationResult.Success; - } return result; } diff --git a/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs b/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs new file mode 100644 index 00000000..1f9cc7ed --- /dev/null +++ b/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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.Server.Contracts.Data +{ + /// + /// Contains all information required to establish a connection with a server. + /// + public class ConnectionInfo + { + /// + /// The API of the server as JSON string. + /// + public string Api { get; set; } + + /// + /// The connection token for authentication with the server. + /// + public string ConnectionToken { get; set; } + + /// + /// The OAuth2 token for authentication with the server. + /// + public string Oauth2Token { get; set; } + } +} diff --git a/SafeExamBrowser.Server.Contracts/Exam.cs b/SafeExamBrowser.Server.Contracts/Data/Exam.cs similarity index 94% rename from SafeExamBrowser.Server.Contracts/Exam.cs rename to SafeExamBrowser.Server.Contracts/Data/Exam.cs index de303948..3d93608d 100644 --- a/SafeExamBrowser.Server.Contracts/Exam.cs +++ b/SafeExamBrowser.Server.Contracts/Data/Exam.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Server.Contracts +namespace SafeExamBrowser.Server.Contracts.Data { /// /// Defines a server exam. diff --git a/SafeExamBrowser.Server.Contracts/ServerResponse.cs b/SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs similarity index 96% rename from SafeExamBrowser.Server.Contracts/ServerResponse.cs rename to SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs index 6b7fe155..5f9b4631 100644 --- a/SafeExamBrowser.Server.Contracts/ServerResponse.cs +++ b/SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Server.Contracts +namespace SafeExamBrowser.Server.Contracts.Data { /// /// Defines the result of a communication with a SEB server. diff --git a/SafeExamBrowser.Server.Contracts/IServerProxy.cs b/SafeExamBrowser.Server.Contracts/IServerProxy.cs index 157efcdb..eb779e1f 100644 --- a/SafeExamBrowser.Server.Contracts/IServerProxy.cs +++ b/SafeExamBrowser.Server.Contracts/IServerProxy.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings.Server; namespace SafeExamBrowser.Server.Contracts @@ -18,33 +19,42 @@ namespace SafeExamBrowser.Server.Contracts public interface IServerProxy { /// - /// TODO: Return API as well or re-load in proxy instance of client? - /// Attempts to initialize a connection to the server. If successful, returns a OAuth2 token as response value. + /// Attempts to initialize a connection to the server. /// - ServerResponse Connect(); + ServerResponse Connect(); /// - /// + /// TODO /// ServerResponse Disconnect(); /// - /// + /// Retrieves a list of all currently available exams. /// ServerResponse> GetAvailableExams(); /// - /// + /// Retrieves the URI of the configuration file for the given exam. /// ServerResponse GetConfigurationFor(Exam exam); + /// + /// Retrieves the information required to establish a connection with this server. + /// + ConnectionInfo GetConnectionInfo(); + /// /// Initializes the server settings to be used for communication. /// void Initialize(ServerSettings settings); /// - /// + /// Initializes the configuration and server settings to be used for communication. + /// + void Initialize(string api, string connectionToken, string oauth2Token, ServerSettings settings); + + /// + /// TODO /// ServerResponse SendSessionInfo(string sessionId); } diff --git a/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj b/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj index 9cf9979c..b5e6086e 100644 --- a/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj +++ b/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj @@ -54,10 +54,11 @@ - + + - + diff --git a/SafeExamBrowser.Server/Data/ApiVersion1.cs b/SafeExamBrowser.Server/Data/ApiVersion1.cs index 23df1ca8..3d723fde 100644 --- a/SafeExamBrowser.Server/Data/ApiVersion1.cs +++ b/SafeExamBrowser.Server/Data/ApiVersion1.cs @@ -11,13 +11,9 @@ namespace SafeExamBrowser.Server.Data internal class ApiVersion1 { public string AccessTokenEndpoint { get; set; } - public string HandshakeEndpoint { get; set; } - public string ConfigurationEndpoint { get; set; } - public string PingEndpoint { get; set; } - public string LogEndpoint { get; set; } } } diff --git a/SafeExamBrowser.Server/ServerProxy.cs b/SafeExamBrowser.Server/ServerProxy.cs index 0bf55d62..9e2c24a0 100644 --- a/SafeExamBrowser.Server/ServerProxy.cs +++ b/SafeExamBrowser.Server/ServerProxy.cs @@ -19,6 +19,7 @@ using Newtonsoft.Json.Linq; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Server.Data; using SafeExamBrowser.Settings.Server; @@ -42,7 +43,7 @@ namespace SafeExamBrowser.Server this.logger = logger; } - public ServerResponse Connect() + public ServerResponse Connect() { var success = TryExecute(HttpMethod.Get, settings.ApiUrl, out var response); var message = ToString(response); @@ -73,7 +74,7 @@ namespace SafeExamBrowser.Server logger.Error("Failed to load server API!"); } - return new ServerResponse(success, oauth2Token, message); + return new ServerResponse(success, message); } public ServerResponse Disconnect() @@ -151,6 +152,16 @@ namespace SafeExamBrowser.Server return new ServerResponse(success, uri, message); } + public ConnectionInfo GetConnectionInfo() + { + return new ConnectionInfo + { + Api = JsonConvert.SerializeObject(api), + ConnectionToken = connectionToken, + Oauth2Token = oauth2Token + }; + } + public void Initialize(ServerSettings settings) { this.settings = settings; @@ -162,6 +173,15 @@ namespace SafeExamBrowser.Server } } + public void Initialize(string api, string connectionToken, string oauth2Token, ServerSettings settings) + { + this.api = JsonConvert.DeserializeObject(api); + this.connectionToken = connectionToken; + this.oauth2Token = oauth2Token; + + Initialize(settings); + } + public ServerResponse SendSessionInfo(string sessionId) { return new ServerResponse(false, "TODO!"); @@ -324,7 +344,7 @@ namespace SafeExamBrowser.Server } catch (TaskCanceledException) { - logger.Error($"Request {request.Method} '{request.RequestUri}' did not complete within {settings.RequestTimeout}ms!"); + logger.Debug($"Request {request.Method} '{request.RequestUri}' did not complete within {settings.RequestTimeout}ms!"); break; } catch (Exception e) diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs index 70418550..08d648f2 100644 --- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs @@ -12,7 +12,7 @@ using SafeExamBrowser.Client.Contracts; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs index db4190bf..7c9cf931 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data { diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index 41fde627..e1d1c89a 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -16,7 +16,7 @@ using SafeExamBrowser.Client.Contracts; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs index 5e31bb55..7d24f079 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using SafeExamBrowser.I18n.Contracts; -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.UserInterface.Contracts.Windows; using SafeExamBrowser.UserInterface.Contracts.Windows.Data; diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs index 58e05475..6c09dd43 100644 --- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs @@ -16,7 +16,7 @@ using SafeExamBrowser.Client.Contracts; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.Server.Contracts; +using SafeExamBrowser.Server.Contracts.Data; using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard;