Continued with taskbar implementation, but ran into WPF's UI thread / UI element restriction. Current state does not work, as UI elements cannot be created on a non-UI thread. Must find proper solution to this problem...
This commit is contained in:
		
							parent
							
								
									16ba40e9a7
								
							
						
					
					
						commit
						8adb72bb0f
					
				
					 44 changed files with 515 additions and 54 deletions
				
			
		
							
								
								
									
										19
									
								
								SafeExamBrowser.Browser/BrowserApplicationInfo.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								SafeExamBrowser.Browser/BrowserApplicationInfo.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| /* | ||||
|  * 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.Configuration; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Browser | ||||
| { | ||||
| 	public class BrowserApplicationInfo : IApplicationInfo | ||||
| 	{ | ||||
| 		public string Name => "Chromium Browser"; | ||||
| 		public string Tooltip => Name; | ||||
| 		public IApplicationIconResource IconResource { get; } = new BrowserIconResource(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										19
									
								
								SafeExamBrowser.Browser/BrowserIconResource.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								SafeExamBrowser.Browser/BrowserIconResource.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Browser | ||||
| { | ||||
| 	public class BrowserIconResource : IApplicationIconResource | ||||
| 	{ | ||||
| 		public Uri Uri => new Uri("pack://application:,,,/SafeExamBrowser.Browser;component/Images/ChromiumLogo.png"); | ||||
| 		public bool IsUriResource => true; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								SafeExamBrowser.Browser/Images/ChromiumLogo.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								SafeExamBrowser.Browser/Images/ChromiumLogo.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 129 KiB | 
|  | @ -0,0 +1,2 @@ | |||
| The "Chromium 11 Logo" by The Chromium Development Documentation Project / The Chromium Authors is licensed under | ||||
| Creative Commons attribution 2.5. Source: https://commons.wikimedia.org/wiki/File:Chromium_11_Logo.svg | ||||
							
								
								
									
										36
									
								
								SafeExamBrowser.Browser/Properties/AssemblyInfo.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								SafeExamBrowser.Browser/Properties/AssemblyInfo.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| using System.Reflection; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| // 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.Browser")] | ||||
| [assembly: AssemblyDescription("")] | ||||
| [assembly: AssemblyConfiguration("")] | ||||
| [assembly: AssemblyCompany("")] | ||||
| [assembly: AssemblyProduct("SafeExamBrowser.Browser")] | ||||
| [assembly: AssemblyCopyright("Copyright ©  2017")] | ||||
| [assembly: AssemblyTrademark("")] | ||||
| [assembly: AssemblyCulture("")] | ||||
| 
 | ||||
| // 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 | ||||
| // COM, set the ComVisible attribute to true on that type. | ||||
| [assembly: ComVisible(false)] | ||||
| 
 | ||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | ||||
| [assembly: Guid("04e653f1-98e6-4e34-9dd7-7f2bc1a8b767")] | ||||
| 
 | ||||
| // Version information for an assembly consists of the following four values: | ||||
| // | ||||
| //      Major Version | ||||
| //      Minor Version | ||||
| //      Build Number | ||||
| //      Revision | ||||
| // | ||||
| // 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")] | ||||
							
								
								
									
										59
									
								
								SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
|     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||||
|     <ProjectGuid>{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}</ProjectGuid> | ||||
|     <OutputType>Library</OutputType> | ||||
|     <AppDesignerFolder>Properties</AppDesignerFolder> | ||||
|     <RootNamespace>SafeExamBrowser.Browser</RootNamespace> | ||||
|     <AssemblyName>SafeExamBrowser.Browser</AssemblyName> | ||||
|     <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | ||||
|     <FileAlignment>512</FileAlignment> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||||
|     <DebugSymbols>true</DebugSymbols> | ||||
|     <DebugType>full</DebugType> | ||||
|     <Optimize>false</Optimize> | ||||
|     <OutputPath>bin\Debug\</OutputPath> | ||||
|     <DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||||
|     <DebugType>pdbonly</DebugType> | ||||
|     <Optimize>true</Optimize> | ||||
|     <OutputPath>bin\Release\</OutputPath> | ||||
|     <DefineConstants>TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|     <Reference Include="System.Data.DataSetExtensions" /> | ||||
|     <Reference Include="Microsoft.CSharp" /> | ||||
|     <Reference Include="System.Data" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="BrowserApplicationInfo.cs" /> | ||||
|     <Compile Include="BrowserIconResource.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj"> | ||||
|       <Project>{47DA5933-BEF8-4729-94E6-ABDE2DB12262}</Project> | ||||
|       <Name>SafeExamBrowser.Contracts</Name> | ||||
|     </ProjectReference> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup /> | ||||
|   <ItemGroup> | ||||
|     <Content Include="Images\ChromiumLogo_Attribution.txt" /> | ||||
|     <Resource Include="Images\ChromiumLogo.png" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
| </Project> | ||||
|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * 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.UserInterface; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Behaviour | ||||
| { | ||||
| 	public interface IApplicationController | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The handler to be executed when an application's taskbar button gets clicked. | ||||
| 		/// </summary> | ||||
| 		TaskbarButtonClickHandler OnClick { get; } | ||||
| 	} | ||||
| } | ||||
|  | @ -6,10 +6,13 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Configuration | ||||
| namespace SafeExamBrowser.Contracts.Behaviour | ||||
| { | ||||
| 	public interface IShutdownController | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Reverts any changes performed during the startup or runtime and releases all used resources. | ||||
| 		/// </summary> | ||||
| 		void FinalizeApplication(); | ||||
| 	} | ||||
| } | ||||
|  | @ -6,10 +6,14 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Configuration | ||||
| namespace SafeExamBrowser.Contracts.Behaviour | ||||
| { | ||||
| 	public interface IStartupController | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Tries to initialize the application. Returns <c>true</c> if the initialization was successful, | ||||
| 		/// <c>false</c> otherwise. | ||||
| 		/// </summary> | ||||
| 		bool TryInitializeApplication(); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,25 @@ | |||
| /* | ||||
|  * 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 IApplicationIconResource | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The <c>Uri</c> pointing to the application icon. | ||||
| 		/// </summary> | ||||
| 		Uri Uri { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Indicates whether the icon resource consists of a <c>Uri</c>. | ||||
| 		/// </summary> | ||||
| 		bool IsUriResource { get; } | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										28
									
								
								SafeExamBrowser.Contracts/Configuration/IApplicationInfo.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								SafeExamBrowser.Contracts/Configuration/IApplicationInfo.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /* | ||||
|  * 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 IApplicationInfo | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The name of the application. | ||||
| 		/// </summary> | ||||
| 		string Name { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The tooltip for the application. | ||||
| 		/// </summary> | ||||
| 		string Tooltip { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The resource providing the application icon. | ||||
| 		/// </summary> | ||||
| 		IApplicationIconResource IconResource { get; } | ||||
| 	} | ||||
| } | ||||
|  | @ -10,9 +10,24 @@ namespace SafeExamBrowser.Contracts.Configuration | |||
| { | ||||
| 	public interface ISettings | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The copyright information for the application, to be displayed in e.g. the log or the splash screen. | ||||
| 		/// </summary> | ||||
| 		string CopyrightInfo { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The path where the log files are to be stored. | ||||
| 		/// </summary> | ||||
| 		string LogFolderPath { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The information to be printed at the beginning of the application log. | ||||
| 		/// </summary> | ||||
| 		string LogHeader { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The program version of the application. | ||||
| 		/// </summary> | ||||
| 		string ProgramVersion { get; } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -10,6 +10,10 @@ namespace SafeExamBrowser.Contracts.I18n | |||
| { | ||||
| 	public interface IText | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Gets the text associated with the specified key. If the key was not found, a default text indicating | ||||
| 		/// that the given key is not configured shall be returned. | ||||
| 		/// </summary> | ||||
| 		string Get(Key key); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -12,6 +12,9 @@ namespace SafeExamBrowser.Contracts.I18n | |||
| { | ||||
| 	public interface ITextResource | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Loads all text data from a resource. | ||||
| 		/// </summary> | ||||
| 		IDictionary<Key, string> LoadText(); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,9 @@ | |||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.I18n | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Defines all text components of the user interface. | ||||
| 	/// </summary> | ||||
| 	public enum Key | ||||
| 	{ | ||||
| 		MessageBox_ShutdownError, | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ using System; | |||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Logging | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Defines a content element of the application log. | ||||
| 	/// </summary> | ||||
| 	public interface ILogContent : ICloneable | ||||
| 	{ | ||||
| 	} | ||||
|  |  | |||
|  | @ -12,9 +12,24 @@ namespace SafeExamBrowser.Contracts.Logging | |||
| { | ||||
| 	public interface ILogMessage : ILogContent | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The date when the message was logged. | ||||
| 		/// </summary> | ||||
| 		DateTime DateTime { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The severity of the message. | ||||
| 		/// </summary> | ||||
| 		LogLevel Severity { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The message itself. | ||||
| 		/// </summary> | ||||
| 		string Message { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Information about the thread on which the message was logged. | ||||
| 		/// </summary> | ||||
| 		IThreadInfo ThreadInfo { get; } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ namespace SafeExamBrowser.Contracts.Logging | |||
| { | ||||
| 	public interface ILogObserver | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Notifies an observer once new content has been added to the application log. | ||||
| 		/// </summary> | ||||
| 		void Notify(ILogContent content); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ namespace SafeExamBrowser.Contracts.Logging | |||
| { | ||||
| 	public interface ILogText : ILogContent | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The raw text to be appended to the application log. | ||||
| 		/// </summary> | ||||
| 		string Text { get; } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -13,14 +13,50 @@ namespace SafeExamBrowser.Contracts.Logging | |||
| { | ||||
| 	public interface ILogger | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Logs the given message with severity <b>INFO</b>. | ||||
| 		/// </summary> | ||||
| 		void Info(string message); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Logs the given message with severity <b>WARNING</b>. | ||||
| 		/// </summary> | ||||
| 		void Warn(string message); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Logs the given message with severity <b>ERROR</b>. | ||||
| 		/// </summary> | ||||
| 		void Error(string message); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Logs the given message with severity <b>ERROR</b> and includes information about | ||||
| 		/// the specified exception (i.e. type, message and stacktrace). | ||||
| 		/// </summary> | ||||
| 		void Error(string message, Exception exception); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Logs the given message as raw text. | ||||
| 		/// </summary> | ||||
| 		void Log(string message); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Appends the given content to the log. | ||||
| 		/// </summary> | ||||
| 		void Log(ILogContent content); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Suscribes an observer to the application log. | ||||
| 		/// </summary> | ||||
| 		void Subscribe(ILogObserver observer); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Unsubscribes an observer from the application log. | ||||
| 		/// </summary> | ||||
| 		void Unsubscribe(ILogObserver observer); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Returns a copy of the current log content. | ||||
| 		/// </summary> | ||||
| 		IList<ILogContent> GetLog(); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -12,8 +12,19 @@ namespace SafeExamBrowser.Contracts.Logging | |||
| { | ||||
| 	public interface IThreadInfo : ICloneable | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// The id of the thread. | ||||
| 		/// </summary> | ||||
| 		int Id { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The thread's name. | ||||
| 		/// </summary> | ||||
| 		string Name { get; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// A flag indicating whether the thread has a name. | ||||
| 		/// </summary> | ||||
| 		bool HasName { get; } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,9 @@ | |||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Logging | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Defines the severity levels of the application log. | ||||
| 	/// </summary> | ||||
| 	public enum LogLevel | ||||
| 	{ | ||||
| 		Info = 1, | ||||
|  |  | |||
|  | @ -40,9 +40,12 @@ | |||
|     <Reference Include="System.Xml" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="Behaviour\IApplicationController.cs" /> | ||||
|     <Compile Include="Configuration\IApplicationIconResource.cs" /> | ||||
|     <Compile Include="Configuration\IApplicationInfo.cs" /> | ||||
|     <Compile Include="Configuration\ISettings.cs" /> | ||||
|     <Compile Include="Configuration\IShutdownController.cs" /> | ||||
|     <Compile Include="Configuration\IStartupController.cs" /> | ||||
|     <Compile Include="Behaviour\IShutdownController.cs" /> | ||||
|     <Compile Include="Behaviour\IStartupController.cs" /> | ||||
|     <Compile Include="I18n\IText.cs" /> | ||||
|     <Compile Include="I18n\Key.cs" /> | ||||
|     <Compile Include="Logging\ILogContent.cs" /> | ||||
|  | @ -58,6 +61,7 @@ | |||
|     <Compile Include="I18n\ITextResource.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|     <Compile Include="UserInterface\ITaskbarButton.cs" /> | ||||
|     <Compile Include="UserInterface\IUiElementFactory.cs" /> | ||||
|     <Compile Include="UserInterface\MessageBoxAction.cs" /> | ||||
|     <Compile Include="UserInterface\MessageBoxIcon.cs" /> | ||||
|   </ItemGroup> | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ namespace SafeExamBrowser.Contracts.UserInterface | |||
| { | ||||
| 	public interface IMessageBox | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Shows a message box according to the specified parameters. | ||||
| 		/// </summary> | ||||
| 		void Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -12,9 +12,24 @@ namespace SafeExamBrowser.Contracts.UserInterface | |||
| { | ||||
| 	public interface ISplashScreen : ILogObserver | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Closes the splash screen. | ||||
| 		/// </summary> | ||||
| 		void Close(); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Set the maximum of the splash screen's progress bar. | ||||
| 		/// </summary> | ||||
| 		void SetMaxProgress(int max); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Shows the splash screen to the user. | ||||
| 		/// </summary> | ||||
| 		void Show(); | ||||
| 		void UpdateProgress(); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Updates the progress bar of the splash screen according to the specified amount. | ||||
| 		/// </summary> | ||||
| 		void UpdateProgress(int amount = 1); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -10,9 +10,24 @@ namespace SafeExamBrowser.Contracts.UserInterface | |||
| { | ||||
| 	public interface ITaskbar | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Adds the given application button to the taskbar. | ||||
| 		/// </summary> | ||||
| 		void AddButton(ITaskbarButton button); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Moves the taskbar to the given location on the screen. | ||||
| 		/// </summary> | ||||
| 		void SetPosition(int x, int y); | ||||
| 		void SetSize(int widht, int height); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Sets the size of the taskbar. | ||||
| 		/// </summary> | ||||
| 		void SetSize(int width, int height); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Displays the taskbar on the screen. | ||||
| 		/// </summary> | ||||
| 		void Show(); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -6,12 +6,31 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| using System; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.UserInterface | ||||
| { | ||||
| 	public delegate void TaskbarButtonClickHandler(); | ||||
| 	public delegate void TaskbarButtonClickHandler(Guid? instanceId = null); | ||||
| 
 | ||||
| 	public interface ITaskbarButton | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// OnClick handler, executed when the user clicks on the application button. If multiple instances of | ||||
| 		/// an application are active, the handler is only executed when the user selects one of the instances. | ||||
| 		/// </summary> | ||||
| 		event TaskbarButtonClickHandler OnClick; | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Registers a new instance of an application, to be displayed when the user clicks the taskbar button. | ||||
| 		/// </summary> | ||||
| 		/// <param name="id">The identifier for the application instance.</param> | ||||
| 		/// <param name="title">An optional title to be displayed (if multiple instances are active).</param> | ||||
| 		void RegisterInstance(Guid id, string title = null); | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Unregisters an application instance, e.g. if it gets closed. | ||||
| 		/// </summary> | ||||
| 		/// <param name="id">The identifier for the application instance.</param> | ||||
| 		void UnregisterInstance(Guid id); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										20
									
								
								SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * 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.Configuration; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.UserInterface | ||||
| { | ||||
| 	public interface IUiElementFactory | ||||
| 	{ | ||||
| 		/// <summary> | ||||
| 		/// Creates a taskbar button, initialized with the given application information. | ||||
| 		/// </summary> | ||||
| 		ITaskbarButton CreateButton(IApplicationInfo info); | ||||
| 	} | ||||
| } | ||||
|  | @ -8,6 +8,9 @@ | |||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.UserInterface | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Defines all actions available for a message box. | ||||
| 	/// </summary> | ||||
| 	public enum MessageBoxAction | ||||
| 	{ | ||||
| 		Confirm | ||||
|  |  | |||
|  | @ -8,6 +8,9 @@ | |||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.UserInterface | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Defines all icons available in a message box. | ||||
| 	/// </summary> | ||||
| 	public enum MessageBoxIcon | ||||
| 	{ | ||||
| 		Information, | ||||
|  |  | |||
|  | @ -7,12 +7,12 @@ | |||
|  */ | ||||
| 
 | ||||
| using System; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.Behaviour; | ||||
| using SafeExamBrowser.Contracts.I18n; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| using SafeExamBrowser.Contracts.UserInterface; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Core.Configuration | ||||
| namespace SafeExamBrowser.Core.Behaviour | ||||
| { | ||||
| 	public class ShutdownController : IShutdownController | ||||
| 	{ | ||||
|  | @ -8,30 +8,35 @@ | |||
| 
 | ||||
| using System; | ||||
| using System.Threading; | ||||
| using SafeExamBrowser.Contracts.Behaviour; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.I18n; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| using SafeExamBrowser.Contracts.UserInterface; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Core.Configuration | ||||
| namespace SafeExamBrowser.Core.Behaviour | ||||
| { | ||||
| 	public class StartupController : IStartupController | ||||
| 	{ | ||||
| 		private IApplicationInfo browserInfo; | ||||
| 		private ILogger logger; | ||||
| 		private IMessageBox messageBox; | ||||
| 		private ISettings settings; | ||||
| 		private ISplashScreen splashScreen; | ||||
| 		private ITaskbar taskbar; | ||||
| 		private IText text; | ||||
| 		private IUiElementFactory uiFactory; | ||||
| 
 | ||||
| 		public StartupController(ILogger logger, IMessageBox messageBox, ISettings settings, ISplashScreen splashScreen, ITaskbar taskbar, IText text) | ||||
| 		public StartupController(IApplicationInfo browserInfo, ILogger logger, IMessageBox messageBox, ISettings settings, ISplashScreen splashScreen, ITaskbar taskbar, IText text, IUiElementFactory uiFactory) | ||||
| 		{ | ||||
| 			this.browserInfo = browserInfo; | ||||
| 			this.logger = logger; | ||||
| 			this.messageBox = messageBox; | ||||
| 			this.settings = settings; | ||||
| 			this.splashScreen = splashScreen; | ||||
| 			this.taskbar = taskbar; | ||||
| 			this.text = text; | ||||
| 			this.uiFactory = uiFactory; | ||||
| 		} | ||||
| 
 | ||||
| 		public bool TryInitializeApplication() | ||||
|  | @ -41,10 +46,15 @@ namespace SafeExamBrowser.Core.Configuration | |||
| 				logger.Log(settings.LogHeader); | ||||
| 				logger.Log($"{Environment.NewLine}# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}{Environment.NewLine}"); | ||||
| 				logger.Info("Initiating startup procedure."); | ||||
| 
 | ||||
| 				logger.Subscribe(splashScreen); | ||||
| 
 | ||||
| 				splashScreen.SetMaxProgress(4); | ||||
| 				splashScreen.SetMaxProgress(3); | ||||
| 				logger.Info("Initializing browser."); | ||||
| 
 | ||||
| 				var browserButton = uiFactory.CreateButton(browserInfo); | ||||
| 
 | ||||
| 				taskbar.AddButton(browserButton); | ||||
| 
 | ||||
| 				splashScreen.UpdateProgress(); | ||||
| 
 | ||||
| 				// TODO (depending on specification): | ||||
|  | @ -72,8 +82,6 @@ namespace SafeExamBrowser.Core.Configuration | |||
| 				logger.Info("Closing splash screen."); | ||||
| 
 | ||||
| 				Thread.Sleep(1000); | ||||
| 
 | ||||
| 				splashScreen.UpdateProgress(); | ||||
| 				logger.Unsubscribe(splashScreen); | ||||
| 				logger.Info("Application successfully initialized!"); | ||||
| 
 | ||||
|  | @ -20,15 +20,10 @@ namespace SafeExamBrowser.Core.Entities | |||
| 
 | ||||
| 		public LogMessage(DateTime dateTime, LogLevel severity, string message, IThreadInfo threadInfo) | ||||
| 		{ | ||||
| 			if (threadInfo == null) | ||||
| 			{ | ||||
| 				throw new ArgumentNullException(nameof(threadInfo)); | ||||
| 			} | ||||
| 
 | ||||
| 			DateTime = dateTime; | ||||
| 			Severity = severity; | ||||
| 			Message = message; | ||||
| 			ThreadInfo = threadInfo; | ||||
| 			ThreadInfo = threadInfo ?? throw new ArgumentNullException(nameof(threadInfo)); | ||||
| 		} | ||||
| 
 | ||||
| 		public object Clone() | ||||
|  |  | |||
|  | @ -41,8 +41,8 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="Configuration\Settings.cs" /> | ||||
|     <Compile Include="Configuration\ShutdownController.cs" /> | ||||
|     <Compile Include="Configuration\StartupController.cs" /> | ||||
|     <Compile Include="Behaviour\ShutdownController.cs" /> | ||||
|     <Compile Include="Behaviour\StartupController.cs" /> | ||||
|     <Compile Include="Logging\LogFileWriter.cs" /> | ||||
|     <Compile Include="Logging\LogMessage.cs" /> | ||||
|     <Compile Include="I18n\Text.cs" /> | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
|              mc:Ignorable="d"  | ||||
|              d:DesignHeight="40" d:DesignWidth="40"> | ||||
|     <Grid> | ||||
|         <Button x:Name="Button" Background="#00000000" BorderThickness="0"> | ||||
|         <Button x:Name="Button" Background="#00000000" BorderThickness="0" Click="Button_Click" Padding="5"> | ||||
|             <Image x:Name="IconImage" /> | ||||
|         </Button> | ||||
|     </Grid> | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| using System; | ||||
| using System.Windows.Controls; | ||||
| using System.Windows.Media.Imaging; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.UserInterface; | ||||
| 
 | ||||
| namespace SafeExamBrowser.UserInterface.Controls | ||||
|  | @ -17,17 +18,44 @@ namespace SafeExamBrowser.UserInterface.Controls | |||
| 	{ | ||||
| 		public event TaskbarButtonClickHandler OnClick; | ||||
| 
 | ||||
| 		public TaskbarButton(string imageUri) | ||||
| 		public TaskbarButton(IApplicationInfo info) | ||||
| 		{ | ||||
| 			InitializeComponent(); | ||||
| 			InitializeButton(info); | ||||
| 		} | ||||
| 
 | ||||
| 			var icon = new BitmapImage(); | ||||
| 		public void RegisterInstance(Guid id, string title = null) | ||||
| 		{ | ||||
| 			throw new NotImplementedException(); | ||||
| 		} | ||||
| 
 | ||||
| 			icon.BeginInit(); | ||||
| 			icon.UriSource = new Uri(imageUri); | ||||
| 			icon.EndInit(); | ||||
| 		public void UnregisterInstance(Guid id) | ||||
| 		{ | ||||
| 			throw new NotImplementedException(); | ||||
| 		} | ||||
| 
 | ||||
| 			IconImage.Source = icon; | ||||
| 		private void InitializeButton(IApplicationInfo info) | ||||
| 		{ | ||||
| 			Button.ToolTip = info.Tooltip; | ||||
| 			 | ||||
| 			if (info.IconResource.IsUriResource) | ||||
| 			{ | ||||
| 				var icon = new BitmapImage(); | ||||
| 
 | ||||
| 				icon.BeginInit(); | ||||
| 				icon.UriSource = info.IconResource.Uri; | ||||
| 				icon.EndInit(); | ||||
| 
 | ||||
| 				IconImage.Source = icon; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private void Button_Click(object sender, System.Windows.RoutedEventArgs e) | ||||
| 		{ | ||||
| 			// TODO | ||||
| 			OnClick?.Invoke(); | ||||
| 
 | ||||
| 			throw new NotImplementedException(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -72,6 +72,7 @@ | |||
|     <Compile Include="Taskbar.xaml.cs"> | ||||
|       <DependentUpon>Taskbar.xaml</DependentUpon> | ||||
|     </Compile> | ||||
|     <Compile Include="UiElementFactory.cs" /> | ||||
|     <Compile Include="ViewModels\DateTimeViewModel.cs" /> | ||||
|     <Compile Include="ViewModels\SplashScreenViewModel.cs" /> | ||||
|     <Compile Include="WpfMessageBox.cs" /> | ||||
|  |  | |||
|  | @ -45,9 +45,9 @@ namespace SafeExamBrowser.UserInterface | |||
| 			model.MaxProgress = max; | ||||
| 		} | ||||
| 
 | ||||
| 		public void UpdateProgress() | ||||
| 		public void UpdateProgress(int amount = 1) | ||||
| 		{ | ||||
| 			model.CurrentProgress += 1; | ||||
| 			model.CurrentProgress += amount; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -32,9 +32,9 @@ namespace SafeExamBrowser.UserInterface | |||
| 			Top = y; | ||||
| 		} | ||||
| 
 | ||||
| 		public void SetSize(int widht, int height) | ||||
| 		public void SetSize(int width, int height) | ||||
| 		{ | ||||
| 			Width = widht; | ||||
| 			Width = width; | ||||
| 			Height = height; | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										22
									
								
								SafeExamBrowser.UserInterface/UiElementFactory.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								SafeExamBrowser.UserInterface/UiElementFactory.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| /* | ||||
|  * 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.Configuration; | ||||
| using SafeExamBrowser.Contracts.UserInterface; | ||||
| using SafeExamBrowser.UserInterface.Controls; | ||||
| 
 | ||||
| namespace SafeExamBrowser.UserInterface | ||||
| { | ||||
| 	public class UiElementFactory : IUiElementFactory | ||||
| 	{ | ||||
| 		public ITaskbarButton CreateButton(IApplicationInfo info) | ||||
| 		{ | ||||
| 			return new TaskbarButton(info); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Core.UnitTe | |||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Contracts", "SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj", "{47DA5933-BEF8-4729-94E6-ABDE2DB12262}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Browser", "SafeExamBrowser.Browser\SafeExamBrowser.Browser.csproj", "{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
|  | @ -45,6 +47,10 @@ Global | |||
| 		{47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ namespace SafeExamBrowser | |||
| 	public class App : Application | ||||
| 	{ | ||||
| 		private static readonly Mutex mutex = new Mutex(true, "safe_exam_browser_single_instance_mutex"); | ||||
| 
 | ||||
| 		private CompositionRoot instances; | ||||
| 
 | ||||
| 		[STAThread] | ||||
|  | @ -78,14 +77,18 @@ namespace SafeExamBrowser | |||
| 
 | ||||
| 			if (success) | ||||
| 			{ | ||||
| 				instances.Taskbar.Dispatcher.Invoke(() => | ||||
| 				Dispatcher.Invoke(() => | ||||
| 				{ | ||||
| 					MainWindow = instances.Taskbar; | ||||
| 					MainWindow.Show(); | ||||
| 				}); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Dispatcher.Invoke(Shutdown); | ||||
| 			} | ||||
| 
 | ||||
| 			instances.SplashScreen.Dispatcher.Invoke(instances.SplashScreen.Close); | ||||
| 			Dispatcher.Invoke(instances.SplashScreen.Close); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -6,10 +6,9 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.I18n; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| using SafeExamBrowser.Contracts.UserInterface; | ||||
| using SafeExamBrowser.Browser; | ||||
| using SafeExamBrowser.Contracts.Behaviour; | ||||
| using SafeExamBrowser.Core.Behaviour; | ||||
| using SafeExamBrowser.Core.Configuration; | ||||
| using SafeExamBrowser.Core.I18n; | ||||
| using SafeExamBrowser.Core.Logging; | ||||
|  | @ -19,28 +18,27 @@ namespace SafeExamBrowser | |||
| { | ||||
| 	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 IStartupController StartupController { get; private set; } | ||||
| 		public IText Text { get; private set; } | ||||
| 
 | ||||
| 		public SplashScreen SplashScreen { get; private set; } | ||||
| 		public Taskbar Taskbar { get; private set; } | ||||
| 
 | ||||
| 		public void BuildObjectGraph() | ||||
| 		{ | ||||
| 			MessageBox = new WpfMessageBox(); | ||||
| 			Settings = new Settings(); | ||||
| 			var browserInfo = new BrowserApplicationInfo(); | ||||
| 			var messageBox = new WpfMessageBox(); | ||||
| 			var settings = new Settings(); | ||||
| 			var logger = new Logger(); | ||||
| 			var text = new Text(new XmlTextResource()); | ||||
| 			var uiFactory = new UiElementFactory(); | ||||
| 			 | ||||
| 			logger.Subscribe(new LogFileWriter(settings)); | ||||
| 
 | ||||
| 			Taskbar = new Taskbar(); | ||||
| 			 | ||||
| 			Logger = new Logger(); | ||||
| 			Logger.Subscribe(new LogFileWriter(Settings)); | ||||
| 
 | ||||
| 			Text = new Text(new XmlTextResource()); | ||||
| 			SplashScreen = new SplashScreen(Settings); | ||||
| 			ShutdownController = new ShutdownController(Logger, MessageBox, Text); | ||||
| 			StartupController = new StartupController(Logger, MessageBox, Settings, SplashScreen, Taskbar, Text); | ||||
| 			SplashScreen = new SplashScreen(settings); | ||||
| 			ShutdownController = new ShutdownController(logger, messageBox, text); | ||||
| 			StartupController = new StartupController(browserInfo, logger, messageBox, settings, SplashScreen, Taskbar, text, uiFactory); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -93,6 +93,10 @@ | |||
|     <None Include="App.config" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\SafeExamBrowser.Browser\SafeExamBrowser.Browser.csproj"> | ||||
|       <Project>{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}</Project> | ||||
|       <Name>SafeExamBrowser.Browser</Name> | ||||
|     </ProjectReference> | ||||
|     <ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj"> | ||||
|       <Project>{47DA5933-BEF8-4729-94E6-ABDE2DB12262}</Project> | ||||
|       <Name>SafeExamBrowser.Contracts</Name> | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Damian Büchel
						Damian Büchel