Continued scaffolding:

- Added splash screen
- Completed logger
- Added startup and shutdown components
This commit is contained in:
Damian Büchel 2017-07-07 15:46:32 +02:00
parent f9723d2c9e
commit 6e44da9497
32 changed files with 584 additions and 85 deletions

View file

@ -11,5 +11,6 @@ namespace SafeExamBrowser.Contracts.Configuration
public interface ISettings
{
string LogFolderPath { get; }
string LogHeader { get; }
}
}

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 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.Contracts.Configuration
{
public interface IShutdownController
{
void FinalizeApplication();
}
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2017 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;
namespace SafeExamBrowser.Contracts.Configuration
{
public interface IStartupController
{
void InitializeApplication(Action terminationCallback);
}
}

View file

@ -10,7 +10,11 @@ namespace SafeExamBrowser.Contracts.I18n
{
public enum Key
{
MessageBox_ShutdownError,
MessageBox_ShutdownErrorTitle,
MessageBox_SingleInstance,
MessageBox_SingleInstanceTitle
MessageBox_SingleInstanceTitle,
MessageBox_StartupError,
MessageBox_StartupErrorTitle
}
}

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2017 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;
namespace SafeExamBrowser.Contracts.Logging
{
public interface ILogContent : ICloneable
{
}
}

View file

@ -10,10 +10,11 @@ using System;
namespace SafeExamBrowser.Contracts.Logging
{
public interface ILogMessage : ICloneable
public interface ILogMessage : ILogContent
{
DateTime DateTime { get; }
LogLevel Severity { get; }
string Message { get; }
int ThreadId { get; }
}
}

View file

@ -10,6 +10,6 @@ namespace SafeExamBrowser.Contracts.Logging
{
public interface ILogObserver
{
void Notify(ILogMessage message);
void Notify(ILogContent content);
}
}

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 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.Contracts.Logging
{
public interface ILogText : ILogContent
{
string Text { get; }
}
}

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
namespace SafeExamBrowser.Contracts.Logging
@ -15,8 +16,11 @@ namespace SafeExamBrowser.Contracts.Logging
void Info(string message);
void Warn(string message);
void Error(string message);
void Error(string message, Exception exception);
void Log(string message);
void Log(ILogContent content);
void Subscribe(ILogObserver observer);
void Unsubscribe(ILogObserver observer);
IList<ILogMessage> GetLog();
IList<ILogContent> GetLog();
}
}

View file

@ -41,15 +41,23 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\ISettings.cs" />
<Compile Include="Configuration\IShutdownController.cs" />
<Compile Include="Configuration\IStartupController.cs" />
<Compile Include="I18n\IText.cs" />
<Compile Include="I18n\Key.cs" />
<Compile Include="Logging\ILogContent.cs" />
<Compile Include="Logging\ILogger.cs" />
<Compile Include="Logging\ILogMessage.cs" />
<Compile Include="Logging\ILogObserver.cs" />
<Compile Include="Logging\ILogText.cs" />
<Compile Include="Logging\LogLevel.cs" />
<Compile Include="UserInterface\IMessageBox.cs" />
<Compile Include="UserInterface\ISplashScreen.cs" />
<Compile Include="UserInterface\ITaskbar.cs" />
<Compile Include="I18n\ITextResource.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserInterface\MessageBoxAction.cs" />
<Compile Include="UserInterface\MessageBoxIcon.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 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.Contracts.UserInterface
{
public interface IMessageBox
{
void Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information);
}
}

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2017 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.Contracts.Logging;
namespace SafeExamBrowser.Contracts.UserInterface
{
public interface ISplashScreen : ILogObserver
{
void Show();
void Close();
}
}

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 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.Contracts.UserInterface
{
public enum MessageBoxAction
{
Confirm
}
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2017 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.Contracts.UserInterface
{
public enum MessageBoxIcon
{
Information,
Warning,
Error
}
}

View file

@ -34,14 +34,14 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
Assert.IsTrue(log.Count == 3);
Assert.IsTrue(info.Equals(log[0].Message));
Assert.IsTrue(log[0].Severity == LogLevel.Info);
Assert.IsTrue(info.Equals((log[0] as ILogMessage).Message));
Assert.IsTrue((log[0] as ILogMessage).Severity == LogLevel.Info);
Assert.IsTrue(warn.Equals(log[1].Message));
Assert.IsTrue(log[1].Severity == LogLevel.Warn);
Assert.IsTrue(warn.Equals((log[1] as ILogMessage).Message));
Assert.IsTrue((log[1] as ILogMessage).Severity == LogLevel.Warn);
Assert.IsTrue(error.Equals(log[2].Message));
Assert.IsTrue(log[2].Severity == LogLevel.Error);
Assert.IsTrue(error.Equals((log[2] as ILogMessage).Message));
Assert.IsTrue((log[2] as ILogMessage).Severity == LogLevel.Error);
}
[TestMethod]
@ -107,23 +107,23 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
var sut = new Logger();
var observer = new Mock<ILogObserver>();
var message = "Blubb";
var messages = new List<ILogMessage>();
var messages = new List<ILogContent>();
observer.Setup(o => o.Notify(It.IsAny<ILogMessage>())).Callback<ILogMessage>(m => messages.Add(m));
observer.Setup(o => o.Notify(It.IsAny<ILogContent>())).Callback<ILogContent>(m => messages.Add(m));
sut.Subscribe(observer.Object);
sut.Info(message);
sut.Warn(message);
observer.Verify(o => o.Notify(It.IsAny<ILogMessage>()), Times.Exactly(2));
observer.Verify(o => o.Notify(It.IsAny<ILogContent>()), Times.Exactly(2));
Assert.IsTrue(messages.Count == 2);
Assert.IsTrue(messages[0].Severity == LogLevel.Info);
Assert.IsTrue(message.Equals(messages[0].Message));
Assert.IsTrue((messages[0] as ILogMessage).Severity == LogLevel.Info);
Assert.IsTrue(message.Equals((messages[0] as ILogMessage).Message));
Assert.IsTrue(messages[1].Severity == LogLevel.Warn);
Assert.IsTrue(message.Equals(messages[1].Message));
Assert.IsTrue((messages[1] as ILogMessage).Severity == LogLevel.Warn);
Assert.IsTrue(message.Equals((messages[1] as ILogMessage).Message));
}
[TestMethod]
@ -132,21 +132,21 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
var sut = new Logger();
var observer = new Mock<ILogObserver>();
var message = "Blubb";
var messages = new List<ILogMessage>();
var messages = new List<ILogContent>();
observer.Setup(o => o.Notify(It.IsAny<ILogMessage>())).Callback<ILogMessage>(m => messages.Add(m));
observer.Setup(o => o.Notify(It.IsAny<ILogContent>())).Callback<ILogContent>(m => messages.Add(m));
sut.Subscribe(observer.Object);
sut.Info(message);
sut.Unsubscribe(observer.Object);
sut.Warn(message);
observer.Verify(o => o.Notify(It.IsAny<ILogMessage>()), Times.Once());
observer.Verify(o => o.Notify(It.IsAny<ILogContent>()), Times.Once());
Assert.IsTrue(messages.Count == 1);
Assert.IsTrue(messages[0].Severity == LogLevel.Info);
Assert.IsTrue(message.Equals(messages[0].Message));
Assert.IsTrue((messages[0] as ILogMessage).Severity == LogLevel.Info);
Assert.IsTrue(message.Equals((messages[0] as ILogMessage).Message));
}
}
}

View file

@ -8,6 +8,7 @@
using System;
using System.IO;
using System.Reflection;
using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.Core.Configuration
@ -21,5 +22,27 @@ namespace SafeExamBrowser.Core.Configuration
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs");
}
}
public string LogHeader
{
get
{
var executable = Assembly.GetEntryAssembly();
var newline = Environment.NewLine;
var version = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
var title = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
var copyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright;
var titleLine = $"/* {title}, Version {version}{newline}";
var copyrightLine = $"/* {copyright}{newline}";
var emptyLine = $"/* {newline}";
var license1 = $"/* The source code of this application is subject to the terms of the Mozilla Public{newline}";
var license2 = $"/* License, v. 2.0. If a copy of the MPL was not distributed with this software, You{newline}";
var license3 = $"/* can obtain one at http://mozilla.org/MPL/2.0/.{newline}";
var github = $"/* For more information or to issue bug reports, see https://github.com/SafeExamBrowser.{newline}";
return $"{titleLine}{copyrightLine}{emptyLine}{license1}{license2}{license3}{emptyLine}{github}";
}
}
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2017 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 SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Configuration
{
public class ShutdownController : IShutdownController
{
private ILogger logger;
private IMessageBox messageBox;
private IText text;
public ShutdownController(ILogger logger, IMessageBox messageBox, IText text)
{
this.logger = logger;
this.messageBox = messageBox;
this.text = text;
}
public void FinalizeApplication()
{
try
{
// TODO:
// - Gather TODOs!
}
catch (Exception e)
{
logger.Error($"Failed to finalize application!", e);
messageBox.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
}
}
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2017 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 System.Threading;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Configuration
{
public class StartupController : IStartupController
{
private ILogger logger;
private IMessageBox messageBox;
private ISplashScreen splashScreen;
private IText text;
public StartupController(ILogger logger, IMessageBox messageBox, ISplashScreen splashScreen, IText text)
{
this.logger = logger;
this.messageBox = messageBox;
this.splashScreen = splashScreen;
this.text = text;
}
public void InitializeApplication(Action terminationCallback)
{
try
{
logger.Info("Rendering splash screen.");
logger.Subscribe(splashScreen);
splashScreen.Show();
// TODO (depending on specification):
// - WCF service connection, termination if not available
// TODO:
// - Parse command line arguments
// - Detecting operating system and logging information
// - Logging of all running processes
// - Setting of wallpaper
// - Initialization of taskbar
// - Killing explorer.exer
// - Minimizing all open windows
// - Emptying clipboard
// - Activation of process monitoring
Thread.Sleep(3000);
logger.Info("Baapa-dee boopa-dee!");
Thread.Sleep(3000);
logger.Info("Closing splash screen.");
logger.Unsubscribe(splashScreen);
splashScreen.Close();
logger.Info("Application successfully initialized!");
}
catch (Exception e)
{
logger.Error($"Failed to initialize application!", e);
messageBox.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error);
terminationCallback?.Invoke();
}
}
}
}

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Text>
<MessageBox_ShutdownError>An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...</MessageBox_ShutdownError>
<MessageBox_ShutdownErrorTitle>Shutdown Error</MessageBox_ShutdownErrorTitle>
<MessageBox_SingleInstance>You can only run one instance of SEB at a time.</MessageBox_SingleInstance>
<MessageBox_SingleInstanceTitle>Startup Not Allowed</MessageBox_SingleInstanceTitle>
<MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError>
<MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle>
</Text>

View file

@ -9,7 +9,6 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
@ -22,7 +21,7 @@ namespace SafeExamBrowser.Core.Logging
public LogFileWriter(ISettings settings)
{
var fileName = $"{DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")}.txt";
var fileName = $"{DateTime.Now.ToString("yyyy-MM-dd HH\\hmm\\mss\\s")}.txt";
if (!Directory.Exists(settings.LogFolderPath))
{
@ -32,17 +31,39 @@ namespace SafeExamBrowser.Core.Logging
filePath = Path.Combine(settings.LogFolderPath, fileName);
}
public void Notify(ILogMessage message)
public void Notify(ILogContent content)
{
if (content is ILogText)
{
WriteLogText(content as ILogText);
}
if (content is ILogMessage)
{
WriteLogMessage(content as ILogMessage);
}
}
private void WriteLogText(ILogText text)
{
Write(text.Text);
}
private void WriteLogMessage(ILogMessage message)
{
var date = message.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
var severity = message.Severity.ToString().ToUpper();
Write($"{date} [{message.ThreadId}] - {severity}: {message.Message}");
}
private void Write(string content)
{
lock (@lock)
{
using (var stream = new StreamWriter(filePath, true, Encoding.UTF8))
{
var date = message.DateTime.ToString("yyyy-MM-dd HH:mm:ss");
var threadId = Thread.CurrentThread.ManagedThreadId;
var severity = message.Severity.ToString().ToUpper();
stream.WriteLine($"{date} [{threadId}] - {severity}: {message.Message}");
stream.WriteLine(content);
}
}
}

View file

@ -16,17 +16,19 @@ namespace SafeExamBrowser.Core.Entities
public DateTime DateTime { get; private set; }
public LogLevel Severity { get; private set; }
public string Message { get; private set; }
public int ThreadId { get; private set; }
public LogMessage(DateTime dateTime, LogLevel severity, string message)
public LogMessage(DateTime dateTime, LogLevel severity, int threadId, string message)
{
DateTime = dateTime;
Severity = severity;
Message = message;
ThreadId = threadId;
}
public object Clone()
{
return new LogMessage(DateTime, Severity, Message);
return new LogMessage(DateTime, Severity, ThreadId, Message);
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017 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.Contracts.Logging;
namespace SafeExamBrowser.Core.Logging
{
public class LogText : ILogText
{
public string Text { get; private set; }
public LogText(string text)
{
Text = text;
}
public object Clone()
{
return new LogText(Text);
}
}
}

View file

@ -9,6 +9,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Entities;
@ -17,29 +19,63 @@ namespace SafeExamBrowser.Core.Logging
public class Logger : ILogger
{
private static readonly object @lock = new object();
private readonly IList<ILogMessage> log = new List<ILogMessage>();
private readonly IList<ILogContent> log = new List<ILogContent>();
private readonly IList<ILogObserver> observers = new List<ILogObserver>();
public void Error(string message)
{
Log(LogLevel.Error, message);
}
public void Info(string message)
{
Log(LogLevel.Info, message);
Add(LogLevel.Info, message);
}
public void Warn(string message)
{
Log(LogLevel.Warn, message);
Add(LogLevel.Warn, message);
}
public IList<ILogMessage> GetLog()
public void Error(string message)
{
Add(LogLevel.Error, message);
}
public void Error(string message, Exception exception)
{
var details = new StringBuilder();
details.AppendLine();
details.AppendLine($" Exception Message: {exception.Message}");
details.AppendLine($" Exception Type: {exception.GetType()}");
details.AppendLine();
details.AppendLine(exception.StackTrace);
Add(LogLevel.Error, message);
Add(new LogText(details.ToString()));
}
public void Log(string text)
{
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
Add(new LogText(text));
}
public void Log(ILogContent content)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
Add(content.Clone() as ILogContent);
}
public IList<ILogContent> GetLog()
{
lock (@lock)
{
return log.Select(m => m.Clone() as ILogMessage).ToList();
return log.Select(m => m.Clone() as ILogContent).ToList();
}
}
@ -67,17 +103,22 @@ namespace SafeExamBrowser.Core.Logging
}
}
private void Log(LogLevel severity, string message)
private void Add(LogLevel severity, string message)
{
var threadId = Thread.CurrentThread.ManagedThreadId;
Add(new LogMessage(DateTime.Now, severity, threadId, message));
}
private void Add(ILogContent content)
{
lock (@lock)
{
var entry = new LogMessage(DateTime.Now, severity, message);
log.Add(content);
log.Add(entry);
foreach (var observer in observers)
{
observer.Notify(entry);
observer.Notify(content);
}
}
}

View file

@ -41,11 +41,14 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\Settings.cs" />
<Compile Include="Configuration\ShutdownController.cs" />
<Compile Include="Configuration\StartupController.cs" />
<Compile Include="Logging\LogFileWriter.cs" />
<Compile Include="Logging\LogMessage.cs" />
<Compile Include="I18n\Text.cs" />
<Compile Include="I18n\XmlTextResource.cs" />
<Compile Include="Logging\Logger.cs" />
<Compile Include="Logging\LogText.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -60,9 +60,13 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="WpfMessageBox.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@ -73,6 +77,10 @@
</None>
</ItemGroup>
<ItemGroup>
<Page Include="SplashScreen.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Taskbar.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -0,0 +1,25 @@
<Window x:Class="SafeExamBrowser.UserInterface.SplashScreen"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface"
mc:Ignorable="d"
Title="SplashScreen" Height="200" Width="350" WindowStyle="None" AllowsTransparency="True" Topmost="True"
WindowStartupLocation="CenterScreen" Cursor="Wait">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="180" />
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Logo etc. goes here..." Foreground="White" />
<ProgressBar Grid.Column="0" Grid.Row="1" Minimum="0" Maximum="100" IsIndeterminate="True" IsEnabled="True" Height="20" Background="#00000000" BorderThickness="0" />
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Text="..." Foreground="White" Background="#00000000" />
</Grid>
</Window>

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017 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.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.UserInterface
{
public partial class SplashScreen : Window, ISplashScreen
{
public SplashScreen()
{
InitializeComponent();
}
public void Notify(ILogContent content)
{
// TODO
}
}
}

View file

@ -7,7 +7,7 @@
mc:Ignorable="d"
Title="Taskbar" Height="40" Width="300" WindowStyle="None" AllowsTransparency="True" Topmost="True">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.7" />
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Grid>

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2017 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.Contracts.UserInterface;
namespace SafeExamBrowser.UserInterface
{
public class WpfMessageBox : IMessageBox
{
public void Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information)
{
MessageBox.Show(message, title, ToButton(action), ToImage(icon));
}
private MessageBoxButton ToButton(MessageBoxAction action)
{
switch (action)
{
default:
return MessageBoxButton.OK;
}
}
private MessageBoxImage ToImage(MessageBoxIcon icon)
{
switch (icon)
{
case MessageBoxIcon.Warning:
return MessageBoxImage.Warning;
case MessageBoxIcon.Error:
return MessageBoxImage.Error;
default:
return MessageBoxImage.Information;
}
}
}
}

View file

@ -15,19 +15,14 @@ namespace SafeExamBrowser
{
public class App : Application
{
private static Mutex mutex = new Mutex(true, "safe_exam_browser_single_instance_mutex");
private static readonly Mutex mutex = new Mutex(true, "safe_exam_browser_single_instance_mutex");
[STAThread]
public static void Main()
{
try
{
var compositionRoot = new CompositionRoot();
compositionRoot.InitializeGlobalModules();
compositionRoot.BuildObjectGraph();
StartApplication(compositionRoot);
StartApplication();
}
catch (Exception e)
{
@ -35,24 +30,36 @@ namespace SafeExamBrowser
}
}
private static void StartApplication(CompositionRoot compositionRoot)
private static void StartApplication()
{
compositionRoot.Logger.Info("Testing the log...");
var root = new CompositionRoot();
if (NoInstanceRunning())
root.BuildObjectGraph();
root.Logger.Log(root.Settings.LogHeader);
root.Logger.Log($"# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}{Environment.NewLine}");
if (NoInstanceRunning(root))
{
new App().Run(compositionRoot.Taskbar);
var app = new App();
root.Logger.Info("No instance is running, initiating startup procedure.");
app.Startup += (o, args) => root.StartupController.InitializeApplication(app.Shutdown);
app.Exit += (o, args) => root.ShutdownController.FinalizeApplication();
app.Run(root.Taskbar);
}
else
{
var message = compositionRoot.Text.Get(Key.MessageBox_SingleInstance);
var title = compositionRoot.Text.Get(Key.MessageBox_SingleInstanceTitle);
MessageBox.Show(message, title);
root.Logger.Info("Could not start because of an already running instance.");
root.MessageBox.Show(root.Text.Get(Key.MessageBox_SingleInstance), root.Text.Get(Key.MessageBox_SingleInstanceTitle));
}
root.Logger.Log($"# Application terminating normally at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
private static bool NoInstanceRunning()
private static bool NoInstanceRunning(CompositionRoot root)
{
return mutex.WaitOne(TimeSpan.Zero, true);
}

View file

@ -10,6 +10,7 @@ using System.Windows;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Configuration;
using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging;
@ -17,24 +18,30 @@ using SafeExamBrowser.UserInterface;
namespace SafeExamBrowser
{
class CompositionRoot
internal class CompositionRoot
{
public ILogger Logger { get; private set; }
public IMessageBox MessageBox { get; private set; }
public ISettings Settings { get; private set; }
public IShutdownController ShutdownController { get; set; }
public ISplashScreen SplashScreen { get; private set; }
public IStartupController StartupController { get; private set; }
public IText Text { get; private set; }
public Window Taskbar { get; private set; }
public void InitializeGlobalModules()
{
Settings = new Settings();
Logger = new Logger();
Logger.Subscribe(new LogFileWriter(Settings));
Text = new Text(new XmlTextResource());
}
public void BuildObjectGraph()
{
MessageBox = new WpfMessageBox();
Settings = new Settings();
SplashScreen = new UserInterface.SplashScreen();
Taskbar = new Taskbar();
Text = new Text(new XmlTextResource());
Logger = new Logger();
Logger.Subscribe(new LogFileWriter(Settings));
ShutdownController = new ShutdownController(Logger, MessageBox, Text);
StartupController = new StartupController(Logger, MessageBox, SplashScreen, Text);
}
}
}

View file

@ -1,20 +1,15 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SafeExamBrowser")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SafeExamBrowser")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyTitle("Safe Exam Browser")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("Safe Exam Browser")]
[assembly: AssemblyCopyright("Copyright © 2017 ETH Zürich, Educational Development and Technology (LET)")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
@ -51,5 +46,6 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("3.0.0")]
[assembly: AssemblyFileVersion("3.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0 (beta)")]