SEBWIN-405: Implemented server failure dialog.

This commit is contained in:
Damian Büchel 2020-07-24 18:22:22 +02:00
parent 60e8457033
commit 7915d4dff9
20 changed files with 304 additions and 20 deletions

View file

@ -165,6 +165,11 @@ namespace SafeExamBrowser.I18n.Contracts
PasswordDialog_SettingsPasswordRequired,
PasswordDialog_SettingsPasswordRequiredTitle,
RuntimeWindow_ApplicationRunning,
ServerFailureDialog_Abort,
ServerFailureDialog_Fallback,
ServerFailureDialog_Message,
ServerFailureDialog_Retry,
ServerFailureDialog_Title,
Shell_QuitButton,
SystemControl_AudioDeviceInfo,
SystemControl_AudioDeviceInfoMuted,

View file

@ -453,6 +453,21 @@
<Entry key="RuntimeWindow_ApplicationRunning">
SEB wird ausgeführt.
</Entry>
<Entry key="ServerFailureDialog_Abort">
Abbrechen
</Entry>
<Entry key="ServerFailureDialog_Fallback">
Fallback
</Entry>
<Entry key="ServerFailureDialog_Message">
Bei der Kommunikation mit dem SEB-Server ist ein Fehler aufgetreten.
</Entry>
<Entry key="ServerFailureDialog_Retry">
Wiederholen
</Entry>
<Entry key="ServerFailureDialog_Title">
SEB-Server-Fehler
</Entry>
<Entry key="Shell_QuitButton">
Sitzung beenden
</Entry>

View file

@ -453,6 +453,21 @@
<Entry key="RuntimeWindow_ApplicationRunning">
SEB is running.
</Entry>
<Entry key="ServerFailureDialog_Abort">
Abort
</Entry>
<Entry key="ServerFailureDialog_Fallback">
Fallback
</Entry>
<Entry key="ServerFailureDialog_Message">
An error occurred while trying to communicate with the SEB server.
</Entry>
<Entry key="ServerFailureDialog_Retry">
Retry
</Entry>
<Entry key="ServerFailureDialog_Title">
SEB-Server Error
</Entry>
<Entry key="Shell_QuitButton">
Terminate Session
</Entry>

View file

@ -16,10 +16,12 @@ namespace SafeExamBrowser.Runtime.Operations.Events
public bool Fallback { get; set; }
public string Message { get; set; }
public bool Retry { get; set; }
public bool ShowFallback { get; }
public ServerFailureEventArgs(string message)
public ServerFailureEventArgs(string message, bool showFallback)
{
Message = message;
ShowFallback = showFallback;
}
}
}

View file

@ -70,6 +70,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (status == LoadStatus.Success)
{
Context.Next.Settings = settings;
Context.Next.Settings.Browser.StartUrl = exam.Url;
result = OperationResult.Success;
}
else
@ -116,7 +117,7 @@ namespace SafeExamBrowser.Runtime.Operations
{
var result = OperationResult.Failed;
if (Context.Current.Settings.SessionMode == SessionMode.Server)
if (Context.Current?.Settings.SessionMode == SessionMode.Server)
{
logger.Info("Finalizing server...");
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer);
@ -192,7 +193,7 @@ namespace SafeExamBrowser.Runtime.Operations
private bool Retry(string message, out bool abort, out bool fallback)
{
var args = new ServerFailureEventArgs(message);
var args = new ServerFailureEventArgs(message, Context.Next.Settings.Server.PerformFallback);
ActionRequired?.Invoke(args);

View file

@ -417,7 +417,18 @@ namespace SafeExamBrowser.Runtime
private void AskForServerFailureAction(ServerFailureEventArgs args)
{
// TODO: Also implement mechanism to retrieve selection via client!!
var isStartup = !SessionIsRunning;
var isRunningOnDefaultDesktop = SessionIsRunning && Session.Settings.Security.KioskMode == KioskMode.DisableExplorerShell;
if (isStartup || isRunningOnDefaultDesktop)
{
TryAskForServerFailureActionViaDialog(args);
}
else
{
// TODO: Also implement mechanism to retrieve selection via client!!
// TryAskForServerFailureActionViaClient(args);
}
}
private void AskIfConfigurationSufficient(ConfigurationCompletedEventArgs args)
@ -503,15 +514,23 @@ namespace SafeExamBrowser.Runtime
private void TryAskForExamSelectionViaDialog(ExamSelectionEventArgs args)
{
var message = TextKey.ExamSelectionDialog_Message;
var title = TextKey.ExamSelectionDialog_Title;
var dialog = uiFactory.CreateExamSelectionDialog(text.Get(message), text.Get(title), args.Exams);
var dialog = uiFactory.CreateExamSelectionDialog(args.Exams);
var result = dialog.Show(runtimeWindow);
args.SelectedExam = result.SelectedExam;
args.Success = result.Success;
}
private void TryAskForServerFailureActionViaDialog(ServerFailureEventArgs args)
{
var dialog = uiFactory.CreateServerFailureDialog(args.Message, args.ShowFallback);
var result = dialog.Show(runtimeWindow);
args.Abort = result.Abort;
args.Fallback = result.Fallback;
args.Retry = result.Retry;
}
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)
{
var message = default(TextKey);

View file

@ -18,6 +18,11 @@ namespace SafeExamBrowser.Server.Contracts
/// </summary>
public string Id { get; set; }
/// <summary>
/// The name of the learning management system (LMS) on which the exam is running.
/// </summary>
public string LmsName { get; set; }
/// <summary>
/// The name of the exam.
/// </summary>

View file

@ -223,6 +223,7 @@ namespace SafeExamBrowser.Server
exams.Add(new Exam
{
Id = exam["examId"].Value<string>(),
LmsName = exam["lmsType"].Value<string>(),
Name = exam["name"].Value<string>(),
Url = exam["url"].Value<string>()
});
@ -314,7 +315,7 @@ namespace SafeExamBrowser.Server
private string ToString(HttpResponseMessage response)
{
return $"{(int) response.StatusCode} {response.StatusCode} {response.ReasonPhrase}";
return $"{(int?) response?.StatusCode} {response?.StatusCode} {response?.ReasonPhrase}";
}
}
}

View file

@ -58,7 +58,7 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// <summary>
/// Creates an exam selection dialog for the given exams.
/// </summary>
IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams);
IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams);
/// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer.
@ -98,9 +98,13 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// <summary>
/// Creates a new runtime window which runs on its own thread.
/// </summary>
/// <returns></returns>
IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig);
/// <summary>
/// Creates a new server failure dialog with the given parameters.
/// </summary>
IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback);
/// <summary>
/// Creates a new splash screen which runs on its own thread.
/// </summary>

View file

@ -89,12 +89,14 @@
<Compile Include="Windows\Data\ExamSelectionDialogResult.cs" />
<Compile Include="Windows\Data\LockScreenOption.cs" />
<Compile Include="Windows\Data\LockScreenResult.cs" />
<Compile Include="Windows\Data\ServerFailureDialogResult.cs" />
<Compile Include="Windows\Events\WindowClosingEventHandler.cs" />
<Compile Include="Windows\IExamSelectionDialog.cs" />
<Compile Include="Windows\ILockScreen.cs" />
<Compile Include="Windows\IPasswordDialog.cs" />
<Compile Include="Windows\Data\PasswordDialogResult.cs" />
<Compile Include="Windows\IRuntimeWindow.cs" />
<Compile Include="Windows\IServerFailureDialog.cs" />
<Compile Include="Windows\ISplashScreen.cs" />
<Compile Include="Windows\IWindow.cs" />
</ItemGroup>

View file

@ -0,0 +1,36 @@
/*
* 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.UserInterface.Contracts.Windows.Data
{
/// <summary>
/// Defines the user interaction result of an <see cref="IServerFailureDialog"/>.
/// </summary>
public class ServerFailureDialogResult
{
/// <summary>
/// Indicates whether the user wants to abort the operation.
/// </summary>
public bool Abort { get; set; }
/// <summary>
/// Indicates whether the user wants to performa a fallback.
/// </summary>
public bool Fallback { get; set; }
/// <summary>
/// Indicates whether the user wants to retry the operation.
/// </summary>
public bool Retry { get; set; }
/// <summary>
/// Indicates whether the user confirmed the dialog or not.
/// </summary>
public bool Success { get; set; }
}
}

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows
{
/// <summary>
/// Defines the functionality of an exam selection dialog.
/// The dialog shown to let the user select which server exam to start.
/// </summary>
public interface IExamSelectionDialog
{

View file

@ -0,0 +1,23 @@
/*
* 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.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows
{
/// <summary>
/// The dialog shown in case a communication failure with a server occurs.
/// </summary>
public interface IServerFailureDialog
{
/// <summary>
/// Shows the dialog as topmost window. If a parent window is specified, the dialog is rendered modally for the given parent.
/// </summary>
ServerFailureDialogResult Show(IWindow parent = null);
}
}

View file

@ -168,6 +168,9 @@
<Compile Include="Windows\RuntimeWindow.xaml.cs">
<DependentUpon>RuntimeWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ServerFailureDialog.xaml.cs">
<DependentUpon>ServerFailureDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile>
@ -347,6 +350,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\ServerFailureDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\Taskview.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
}
public IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams)
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(message, title, text, exams));
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
}
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
@ -167,6 +167,11 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
}
public IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
return Application.Current.Dispatcher.Invoke(() => new ServerFailureDialog(info, showFallback, text));
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
var window = default(SplashScreen);

View file

@ -39,7 +39,7 @@
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,5,0" Text="{Binding Id}" />
<TextBlock Margin="0,0,5,0" Text="-" />
<TextBlock FontStyle="Italic" Text="{Binding Url}" />
<TextBlock FontStyle="Italic" Text="{Binding LmsName}" />
</StackPanel>
</StackPanel>
</DataTemplate>

View file

@ -20,12 +20,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
private readonly IText text;
public ExamSelectionDialog(string message, string title, IText text, IEnumerable<Exam> exams)
public ExamSelectionDialog(IEnumerable<Exam> exams, IText text)
{
this.text = text;
InitializeComponent();
InitializeExamSelectionDialog(message, title, exams);
InitializeExamSelectionDialog(exams);
}
public ExamSelectionDialogResult Show(IWindow parent = null)
@ -50,10 +50,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
});
}
private void InitializeExamSelectionDialog(string message, string title, IEnumerable<Exam> exams)
private void InitializeExamSelectionDialog(IEnumerable<Exam> exams)
{
Message.Text = message;
Title = title;
Message.Text = text.Get(TextKey.ExamSelectionDialog_Message);
Title = text.Get(TextKey.ExamSelectionDialog_Title);
WindowStartupLocation = WindowStartupLocation.CenterScreen;
CancelButton.Content = text.Get(TextKey.ExamSelectionDialog_Cancel);

View file

@ -0,0 +1,42 @@
<Window x:Class="SafeExamBrowser.UserInterface.Desktop.Windows.ServerFailureDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Windows"
mc:Ignorable="d" Height="250" Width="450" ResizeMode="NoResize" Topmost="True">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid FocusManager.FocusedElement="{Binding ElementName=RetryButton}">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<fa:ImageAwesome Grid.Column="0" Foreground="LightGray" Icon="Warning" Margin="25" Width="50" />
<WrapPanel Grid.Column="1" Margin="0,0,25,0" Orientation="Vertical" VerticalAlignment="Center">
<TextBlock x:Name="Message" TextWrapping="WrapWithOverflow" />
<TextBlock x:Name="Info" FontFamily="Courier New" Margin="0,10,0,0" TextWrapping="WrapWithOverflow" />
</WrapPanel>
</Grid>
</Grid>
<Grid Grid.Row="1" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<WrapPanel Orientation="Horizontal" Margin="25,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button x:Name="RetryButton" Cursor="Hand" Margin="10,0" Padding="10,5" MinWidth="75" />
<Button x:Name="FallbackButton" Cursor="Hand" Margin="0,0,10,0" Padding="10,5" MinWidth="75" />
<Button x:Name="AbortButton" Cursor="Hand" Padding="10,5" MinWidth="75" />
</WrapPanel>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,96 @@
/*
* 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 System.Windows;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
public partial class ServerFailureDialog : Window, IServerFailureDialog
{
private readonly IText text;
public ServerFailureDialog(string info, bool showFallback, IText text)
{
this.text = text;
InitializeComponent();
InitializeDialog(info, showFallback);
}
public ServerFailureDialogResult Show(IWindow parent = null)
{
return Dispatcher.Invoke(() =>
{
var result = new ServerFailureDialogResult { Success = false };
if (parent is Window)
{
Owner = parent as Window;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
if (ShowDialog() is true)
{
result.Abort = Tag as string == nameof(AbortButton);
result.Fallback = Tag as string == nameof(FallbackButton);
result.Retry = Tag as string == nameof(RetryButton);
result.Success = true;
}
else
{
result.Abort = true;
}
return result;
});
}
private void InitializeDialog(string info, bool showFallback)
{
Info.Text = info;
Message.Text = text.Get(TextKey.ServerFailureDialog_Message);
Title = text.Get(TextKey.ServerFailureDialog_Title);
AbortButton.Click += AbortButton_Click;
AbortButton.Content = text.Get(TextKey.ServerFailureDialog_Abort);
FallbackButton.Click += FallbackButton_Click;
FallbackButton.Content = text.Get(TextKey.ServerFailureDialog_Fallback);
FallbackButton.Visibility = showFallback ? Visibility.Visible : Visibility.Collapsed;
Loaded += (o, args) => Activate();
RetryButton.Click += RetryButton_Click;
RetryButton.Content = text.Get(TextKey.ServerFailureDialog_Retry);
}
private void AbortButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(AbortButton);
Close();
}
private void FallbackButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(FallbackButton);
Close();
}
private void RetryButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(RetryButton);
Close();
}
}
}

View file

@ -82,7 +82,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
}
public IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams)
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{
// TODO
throw new System.NotImplementedException();
@ -168,6 +168,12 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
}
public IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
// TODO
throw new System.NotImplementedException();
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
var window = default(SplashScreen);