Continued scaffolding:
- Added splash screen - Completed logger - Added startup and shutdown components
This commit is contained in:
parent
f9723d2c9e
commit
6e44da9497
32 changed files with 584 additions and 85 deletions
|
@ -11,5 +11,6 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
public interface ISettings
|
||||
{
|
||||
string LogFolderPath { get; }
|
||||
string LogHeader { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
16
SafeExamBrowser.Contracts/Logging/ILogContent.cs
Normal file
16
SafeExamBrowser.Contracts/Logging/ILogContent.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ namespace SafeExamBrowser.Contracts.Logging
|
|||
{
|
||||
public interface ILogObserver
|
||||
{
|
||||
void Notify(ILogMessage message);
|
||||
void Notify(ILogContent content);
|
||||
}
|
||||
}
|
||||
|
|
15
SafeExamBrowser.Contracts/Logging/ILogText.cs
Normal file
15
SafeExamBrowser.Contracts/Logging/ILogText.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
15
SafeExamBrowser.Contracts/UserInterface/IMessageBox.cs
Normal file
15
SafeExamBrowser.Contracts/UserInterface/IMessageBox.cs
Normal 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);
|
||||
}
|
||||
}
|
18
SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
Normal file
18
SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
Normal 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();
|
||||
}
|
||||
}
|
15
SafeExamBrowser.Contracts/UserInterface/MessageBoxAction.cs
Normal file
15
SafeExamBrowser.Contracts/UserInterface/MessageBoxAction.cs
Normal 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
|
||||
}
|
||||
}
|
17
SafeExamBrowser.Contracts/UserInterface/MessageBoxIcon.cs
Normal file
17
SafeExamBrowser.Contracts/UserInterface/MessageBoxIcon.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
44
SafeExamBrowser.Core/Configuration/ShutdownController.cs
Normal file
44
SafeExamBrowser.Core/Configuration/ShutdownController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
SafeExamBrowser.Core/Configuration/StartupController.cs
Normal file
75
SafeExamBrowser.Core/Configuration/StartupController.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
SafeExamBrowser.Core/Logging/LogText.cs
Normal file
27
SafeExamBrowser.Core/Logging/LogText.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
25
SafeExamBrowser.UserInterface/SplashScreen.xaml
Normal file
25
SafeExamBrowser.UserInterface/SplashScreen.xaml
Normal 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>
|
27
SafeExamBrowser.UserInterface/SplashScreen.xaml.cs
Normal file
27
SafeExamBrowser.UserInterface/SplashScreen.xaml.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
43
SafeExamBrowser.UserInterface/WpfMessageBox.cs
Normal file
43
SafeExamBrowser.UserInterface/WpfMessageBox.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)")]
|
||||
|
|
Loading…
Reference in a new issue